<commit_before>/*!
 * \file rounded_rect.hpp
 * \brief file rounded_rect.hpp
 *
 * Copyright 2018 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/vecN.hpp>

namespace fastuidraw
{
/*!\addtogroup Painter
 * @{
 */

  /*!
   * Class to specify the geometry of a rounded rectangle.
   */
  class Rect
  {
  public:
    /*!
     * Conveniance enumeration to name the rounded corner
     * radii of a RoundedRect.
     */
    enum corner_t
      {
        minx_miny_corner,
        minx_maxy_corner,
        maxx_miny_corner,
        maxx_maxy_corner,
      };

    /*!
     * Empty ctor; intializes both \ref m_min_point and
     * \ref m_max_point to (0, 0);
     */
    Rect(void):
      m_min_point(0.0f, 0.0f),
      m_max_point(0.0f, 0.0f)
    {}

    /*!
     * Set \ref m_min_point.
     */
    Rect&
    min_point(const vec2 &p)
    {
      m_min_point = p;
      return *this;
    }

    /*!
     * Set \ref m_min_point.
     */
    Rect&
    min_point(float x, float y)
    {
      m_min_point.x() = x;
      m_min_point.y() = y;
      return *this;
    }

    /*!
     * Set \ref m_max_point.
     */
    Rect&
    max_point(const vec2 &p)
    {
      m_max_point = p;
      return *this;
    }

    /*!
     * Set \ref m_max_point.
     */
    Rect&
    max_point(float x, float y)
    {
      m_max_point.x() = x;
      m_max_point.y() = y;
      return *this;
    }

    /*!
     * Set \ref m_max_point from \ref m_min_point
     * and a size. Equivalent to
     * \code
     * max_point(min_point() + sz)
     * \endcode
     * \param sz size to which to set the Rect
     */
    Rect&
    size(const vec2 &sz)
    {
      m_max_point = m_min_point + sz;
      return *this;
    }

    /*!
     * Set \ref m_max_point from \ref m_min_point
     * and a size. Equivalent to
     * \code
     * max_point(min_point() + vec2(width, height))
     * \endcode
     * \param width width to which to set the rect
     * \param height height to which to set the rect
     */
    Rect&
    size(float width, float height)
    {
      m_max_point.x() = m_min_point.x() + width;
      m_max_point.y() = m_min_point.y() + height;
      return *this;
    }

    /*!
     * Returns the size of the Rect; provided as
     * a conveniance, equivalent to
     * \code
     * m_max_point - m_min_point
     * \endcode
     */
    vec2
    size(void) const
    {
      return m_max_point - m_min_point;
    }

    /*!
     * Returns the width of the Rect, equivalent to
     * \code
     * m_max_point.x() - m_min_point.x();
     * \endcode
     */
    float
    width(void) const
    {
      return m_max_point.x() - m_min_point.x();
    }

    /*!
     * Returns the width of the Rect, equivalent to
     * \code
     * m_max_point.y() - m_min_point.y();
     * \endcode
     */
    float
    height(void) const
    {
      return m_max_point.y() - m_min_point.y();
    }

    /*!
     * Specifies the min-corner of the rectangle
     */
    vec2 m_min_point;

    /*!
     * Specifies the max-corner of the rectangle.
     */
    vec2 m_max_point;
  };
/*! @} */
}
<commit_msg>fastuidraw/painter/rect: more methods of conveniance<commit_after>/*!
 * \file rounded_rect.hpp
 * \brief file rounded_rect.hpp
 *
 * Copyright 2018 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/vecN.hpp>

namespace fastuidraw
{
/*!\addtogroup Painter
 * @{
 */

  /*!
   * Class to specify the geometry of a rounded rectangle.
   */
  class Rect
  {
  public:
    /*!
     * Conveniance enumeration to name the rounded corner
     * radii of a RoundedRect.
     */
    enum corner_t
      {
        minx_miny_corner,
        minx_maxy_corner,
        maxx_miny_corner,
        maxx_maxy_corner,
      };

    /*!
     * Empty ctor; intializes both \ref m_min_point and
     * \ref m_max_point to (0, 0);
     */
    Rect(void):
      m_min_point(0.0f, 0.0f),
      m_max_point(0.0f, 0.0f)
    {}

    /*!
     * Set \ref m_min_point.
     */
    Rect&
    min_point(const vec2 &p)
    {
      m_min_point = p;
      return *this;
    }

    /*!
     * Set \ref m_min_point.
     */
    Rect&
    min_point(float x, float y)
    {
      m_min_point.x() = x;
      m_min_point.y() = y;
      return *this;
    }

    /*!
     * Set \ref m_max_point.
     */
    Rect&
    max_point(const vec2 &p)
    {
      m_max_point = p;
      return *this;
    }

    /*!
     * Set \ref m_max_point.
     */
    Rect&
    max_point(float x, float y)
    {
      m_max_point.x() = x;
      m_max_point.y() = y;
      return *this;
    }

    /*!
     * Translate the Rect, equivalent to
     * \code
     * m_min_point += tr;
     * m_max_point += tr;
     * \endcode
     * \param tr amount by which to transate
     */
    Rect&
    translate(const vec2 &tr)
    {
      m_min_point += tr;
      m_max_point += tr;
      return *this;
    }

    /*!
     * Translate the Rect, equivalent to
     * \code
     * translate(vec2(x, y))
     * \endcode
     * \param tr amount by which to transate
     */
    Rect&
    translate(float x, float y)
    {
      return translate(vec2(x,y));
    }

    /*!
     * Set \ref m_max_point from \ref m_min_point
     * and a size. Equivalent to
     * \code
     * max_point(min_point() + sz)
     * \endcode
     * \param sz size to which to set the Rect
     */
    Rect&
    size(const vec2 &sz)
    {
      m_max_point = m_min_point + sz;
      return *this;
    }

    /*!
     * Set \ref m_max_point from \ref m_min_point
     * and a size. Equivalent to
     * \code
     * max_point(min_point() + vec2(width, height))
     * \endcode
     * \param width width to which to set the rect
     * \param height height to which to set the rect
     */
    Rect&
    size(float width, float height)
    {
      m_max_point.x() = m_min_point.x() + width;
      m_max_point.y() = m_min_point.y() + height;
      return *this;
    }

    /*!
     * Returns the size of the Rect; provided as
     * a conveniance, equivalent to
     * \code
     * m_max_point - m_min_point
     * \endcode
     */
    vec2
    size(void) const
    {
      return m_max_point - m_min_point;
    }

    /*!
     * Set the width of the Rect, equivalent to
     * \code
     * m_max_point.x() = w + m_min_point.x();
     * \endcode
     * \param w value to make the width of the rect
     */
    Rect&
    width(float w)
    {
      m_max_point.x() = w + m_min_point.x();
      return *this;
    }

    /*!
     * Set the height of the Rect, equivalent to
     * \code
     * m_max_point.y() = h + m_min_point.y();
     * \endcode
     * \param h value to make the height of the rect
     */
    Rect&
    height(float h)
    {
      m_max_point.y() = h + m_min_point.y();
      return *this;
    }

    /*!
     * Returns the width of the Rect, equivalent to
     * \code
     * m_max_point.x() - m_min_point.x();
     * \endcode
     */
    float
    width(void) const
    {
      return m_max_point.x() - m_min_point.x();
    }

    /*!
     * Returns the width of the Rect, equivalent to
     * \code
     * m_max_point.y() - m_min_point.y();
     * \endcode
     */
    float
    height(void) const
    {
      return m_max_point.y() - m_min_point.y();
    }

    /*!
     * Specifies the min-corner of the rectangle
     */
    vec2 m_min_point;

    /*!
     * Specifies the max-corner of the rectangle.
     */
    vec2 m_max_point;
  };
/*! @} */
}
<|endoftext|>


<commit_before>//===-- cc1as_main.cpp - Clang Assembler  ---------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This is the entry point to the clang -cc1as functionality, which implements
// the direct interface to the LLVM MC based assembler.
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/Diagnostic.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/CC1AsOptions.h"
#include "clang/Driver/OptTable.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCTargetAsmParser.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/system_error.h"
#include "llvm/Target/TargetData.h"
using namespace clang;
using namespace clang::driver;
using namespace llvm;

namespace {

/// \brief Helper class for representing a single invocation of the assembler.
struct AssemblerInvocation {
  /// @name Target Options
  /// @{

  std::string Triple;

  /// @}
  /// @name Language Options
  /// @{

  std::vector<std::string> IncludePaths;
  unsigned NoInitialTextSection : 1;
  unsigned SaveTemporaryLabels : 1;

  /// @}
  /// @name Frontend Options
  /// @{

  std::string InputFile;
  std::vector<std::string> LLVMArgs;
  std::string OutputPath;
  enum FileType {
    FT_Asm,  ///< Assembly (.s) output, transliterate mode.
    FT_Null, ///< No output, for timing purposes.
    FT_Obj   ///< Object file output.
  };
  FileType OutputType;
  unsigned ShowHelp : 1;
  unsigned ShowVersion : 1;

  /// @}
  /// @name Transliterate Options
  /// @{

  unsigned OutputAsmVariant;
  unsigned ShowEncoding : 1;
  unsigned ShowInst : 1;

  /// @}
  /// @name Assembler Options
  /// @{

  unsigned RelaxAll : 1;
  unsigned NoExecStack : 1;

  /// @}

public:
  AssemblerInvocation() {
    Triple = "";
    NoInitialTextSection = 0;
    InputFile = "-";
    OutputPath = "-";
    OutputType = FT_Asm;
    OutputAsmVariant = 0;
    ShowInst = 0;
    ShowEncoding = 0;
    RelaxAll = 0;
    NoExecStack = 0;
  }

  static void CreateFromArgs(AssemblerInvocation &Res, const char **ArgBegin,
                             const char **ArgEnd, Diagnostic &Diags);
};

}

void AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
                                         const char **ArgBegin,
                                         const char **ArgEnd,
                                         Diagnostic &Diags) {
  using namespace clang::driver::cc1asoptions;
  // Parse the arguments.
  OwningPtr<OptTable> OptTbl(createCC1AsOptTable());
  unsigned MissingArgIndex, MissingArgCount;
  OwningPtr<InputArgList> Args(
    OptTbl->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount));

  // Check for missing argument error.
  if (MissingArgCount)
    Diags.Report(diag::err_drv_missing_argument)
      << Args->getArgString(MissingArgIndex) << MissingArgCount;

  // Issue errors on unknown arguments.
  for (arg_iterator it = Args->filtered_begin(cc1asoptions::OPT_UNKNOWN),
         ie = Args->filtered_end(); it != ie; ++it)
    Diags.Report(diag::err_drv_unknown_argument) << (*it) ->getAsString(*Args);

  // Construct the invocation.

  // Target Options
  Opts.Triple = Triple::normalize(Args->getLastArgValue(OPT_triple));
  if (Opts.Triple.empty()) // Use the host triple if unspecified.
    Opts.Triple = sys::getHostTriple();

  // Language Options
  Opts.IncludePaths = Args->getAllArgValues(OPT_I);
  Opts.NoInitialTextSection = Args->hasArg(OPT_n);
  Opts.SaveTemporaryLabels = Args->hasArg(OPT_L);

  // Frontend Options
  if (Args->hasArg(OPT_INPUT)) {
    bool First = true;
    for (arg_iterator it = Args->filtered_begin(OPT_INPUT),
           ie = Args->filtered_end(); it != ie; ++it, First=false) {
      const Arg *A = it;
      if (First)
        Opts.InputFile = A->getValue(*Args);
      else
        Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(*Args);
    }
  }
  Opts.LLVMArgs = Args->getAllArgValues(OPT_mllvm);
  if (Args->hasArg(OPT_fatal_warnings))
    Opts.LLVMArgs.push_back("-fatal-assembler-warnings");
  Opts.OutputPath = Args->getLastArgValue(OPT_o);
  if (Arg *A = Args->getLastArg(OPT_filetype)) {
    StringRef Name = A->getValue(*Args);
    unsigned OutputType = StringSwitch<unsigned>(Name)
      .Case("asm", FT_Asm)
      .Case("null", FT_Null)
      .Case("obj", FT_Obj)
      .Default(~0U);
    if (OutputType == ~0U)
      Diags.Report(diag::err_drv_invalid_value)
        << A->getAsString(*Args) << Name;
    else
      Opts.OutputType = FileType(OutputType);
  }
  Opts.ShowHelp = Args->hasArg(OPT_help);
  Opts.ShowVersion = Args->hasArg(OPT_version);

  // Transliterate Options
  Opts.OutputAsmVariant = Args->getLastArgIntValue(OPT_output_asm_variant,
                                                   0, Diags);
  Opts.ShowEncoding = Args->hasArg(OPT_show_encoding);
  Opts.ShowInst = Args->hasArg(OPT_show_inst);

  // Assemble Options
  Opts.RelaxAll = Args->hasArg(OPT_relax_all);
  Opts.NoExecStack =  Args->hasArg(OPT_no_exec_stack);
}

static formatted_raw_ostream *GetOutputStream(AssemblerInvocation &Opts,
                                              Diagnostic &Diags,
                                              bool Binary) {
  if (Opts.OutputPath.empty())
    Opts.OutputPath = "-";

  // Make sure that the Out file gets unlinked from the disk if we get a
  // SIGINT.
  if (Opts.OutputPath != "-")
    sys::RemoveFileOnSignal(sys::Path(Opts.OutputPath));

  std::string Error;
  raw_fd_ostream *Out =
    new raw_fd_ostream(Opts.OutputPath.c_str(), Error,
                       (Binary ? raw_fd_ostream::F_Binary : 0));
  if (!Error.empty()) {
    Diags.Report(diag::err_fe_unable_to_open_output)
      << Opts.OutputPath << Error;
    return 0;
  }

  return new formatted_raw_ostream(*Out, formatted_raw_ostream::DELETE_STREAM);
}

static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) {
  // Get the target specific parser.
  std::string Error;
  const Target *TheTarget(TargetRegistry::lookupTarget(Opts.Triple, Error));
  if (!TheTarget) {
    Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
    return false;
  }

  OwningPtr<MemoryBuffer> BufferPtr;
  if (error_code ec = MemoryBuffer::getFileOrSTDIN(Opts.InputFile, BufferPtr)) {
    Error = ec.message();
    Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
    return false;
  }
  MemoryBuffer *Buffer = BufferPtr.take();

  SourceMgr SrcMgr;

  // Tell SrcMgr about this buffer, which is what the parser will pick up.
  SrcMgr.AddNewSourceBuffer(Buffer, SMLoc());

  // Record the location of the include directories so that the lexer can find
  // it later.
  SrcMgr.setIncludeDirs(Opts.IncludePaths);

  OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(Opts.Triple));
  assert(MAI && "Unable to create target asm info!");

  OwningPtr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
  assert(MRI && "Unable to create target register info!");

  bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
  formatted_raw_ostream *Out = GetOutputStream(Opts, Diags, IsBinary);
  if (!Out)
    return false;

  // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
  // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
  OwningPtr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
  MCContext Ctx(*MAI, *MRI, MOFI.get());
  // FIXME: Assembler behavior can change with -static.
  MOFI->InitMCObjectFileInfo(Opts.Triple,
                             Reloc::Default, CodeModel::Default, Ctx);
  if (Opts.SaveTemporaryLabels)
    Ctx.setAllowTemporaryLabels(false);

  OwningPtr<MCStreamer> Str;

  OwningPtr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
  OwningPtr<MCSubtargetInfo>
    STI(TheTarget->createMCSubtargetInfo(Opts.Triple, "", ""));

  // FIXME: There is a bit of code duplication with addPassesToEmitFile.
  if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
    MCInstPrinter *IP =
      TheTarget->createMCInstPrinter(Opts.OutputAsmVariant, *MAI);
    MCCodeEmitter *CE = 0;
    MCAsmBackend *MAB = 0;
    if (Opts.ShowEncoding) {
      CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
      MAB = TheTarget->createMCAsmBackend(Opts.Triple);
    }
    Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true,
                                           /*useLoc*/ true,
                                           /*useCFI*/ true, IP, CE, MAB,
                                           Opts.ShowInst));
  } else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
    Str.reset(createNullStreamer(Ctx));
  } else {
    assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
           "Invalid file type!");
    MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
    MCAsmBackend *MAB = TheTarget->createMCAsmBackend(Opts.Triple);
    Str.reset(TheTarget->createMCObjectStreamer(Opts.Triple, Ctx, *MAB, *Out,
                                                CE, Opts.RelaxAll,
                                                Opts.NoExecStack));
    Str.get()->InitSections();
  }

  OwningPtr<MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx,
                                                  *Str.get(), *MAI));
  OwningPtr<MCTargetAsmParser> TAP(TheTarget->createMCAsmParser(*STI, *Parser));
  if (!TAP) {
    Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
    return false;
  }

  Parser->setTargetParser(*TAP.get());

  bool Success = !Parser->Run(Opts.NoInitialTextSection);

  // Close the output.
  delete Out;

  // Delete output on errors.
  if (!Success && Opts.OutputPath != "-")
    sys::Path(Opts.OutputPath).eraseFromDisk();

  return Success;
}

static void LLVMErrorHandler(void *UserData, const std::string &Message) {
  Diagnostic &Diags = *static_cast<Diagnostic*>(UserData);

  Diags.Report(diag::err_fe_error_backend) << Message;

  // We cannot recover from llvm errors.
  exit(1);
}

int cc1as_main(const char **ArgBegin, const char **ArgEnd,
               const char *Argv0, void *MainAddr) {
  // Print a stack trace if we signal out.
  sys::PrintStackTraceOnErrorSignal();
  PrettyStackTraceProgram X(ArgEnd - ArgBegin, ArgBegin);
  llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.

  // Initialize targets and assembly printers/parsers.
  InitializeAllTargetInfos();
  InitializeAllTargetMCs();
  InitializeAllAsmParsers();

  // Construct our diagnostic client.
  TextDiagnosticPrinter *DiagClient
    = new TextDiagnosticPrinter(errs(), DiagnosticOptions());
  DiagClient->setPrefix("clang -cc1as");
  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
  Diagnostic Diags(DiagID, DiagClient);

  // Set an error handler, so that any LLVM backend diagnostics go through our
  // error handler.
  ScopedFatalErrorHandler FatalErrorHandler
    (LLVMErrorHandler, static_cast<void*>(&Diags));

  // Parse the arguments.
  AssemblerInvocation Asm;
  AssemblerInvocation::CreateFromArgs(Asm, ArgBegin, ArgEnd, Diags);

  // Honor -help.
  if (Asm.ShowHelp) {
    llvm::OwningPtr<driver::OptTable> Opts(driver::createCC1AsOptTable());
    Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler");
    return 0;
  }

  // Honor -version.
  //
  // FIXME: Use a better -version message?
  if (Asm.ShowVersion) {
    llvm::cl::PrintVersionMessage();
    return 0;
  }

  // Honor -mllvm.
  //
  // FIXME: Remove this, one day.
  if (!Asm.LLVMArgs.empty()) {
    unsigned NumArgs = Asm.LLVMArgs.size();
    const char **Args = new const char*[NumArgs + 2];
    Args[0] = "clang (LLVM option parsing)";
    for (unsigned i = 0; i != NumArgs; ++i)
      Args[i + 1] = Asm.LLVMArgs[i].c_str();
    Args[NumArgs + 1] = 0;
    llvm::cl::ParseCommandLineOptions(NumArgs + 1, const_cast<char **>(Args));
  }

  // Execute the invocation, unless there were parsing errors.
  bool Success = false;
  if (!Diags.hasErrorOccurred())
    Success = ExecuteAssembler(Asm, Diags);

  // If any timers were active but haven't been destroyed yet, print their
  // results now.
  TimerGroup::printAll(errs());

  return !Success;
}
<commit_msg>Fix up MCInstPrinter creation to take the new SubtargetInfo parameter (see LLVM r139237)<commit_after>//===-- cc1as_main.cpp - Clang Assembler  ---------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This is the entry point to the clang -cc1as functionality, which implements
// the direct interface to the LLVM MC based assembler.
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/Diagnostic.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/CC1AsOptions.h"
#include "clang/Driver/OptTable.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCTargetAsmParser.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/system_error.h"
#include "llvm/Target/TargetData.h"
using namespace clang;
using namespace clang::driver;
using namespace llvm;

namespace {

/// \brief Helper class for representing a single invocation of the assembler.
struct AssemblerInvocation {
  /// @name Target Options
  /// @{

  std::string Triple;

  /// @}
  /// @name Language Options
  /// @{

  std::vector<std::string> IncludePaths;
  unsigned NoInitialTextSection : 1;
  unsigned SaveTemporaryLabels : 1;

  /// @}
  /// @name Frontend Options
  /// @{

  std::string InputFile;
  std::vector<std::string> LLVMArgs;
  std::string OutputPath;
  enum FileType {
    FT_Asm,  ///< Assembly (.s) output, transliterate mode.
    FT_Null, ///< No output, for timing purposes.
    FT_Obj   ///< Object file output.
  };
  FileType OutputType;
  unsigned ShowHelp : 1;
  unsigned ShowVersion : 1;

  /// @}
  /// @name Transliterate Options
  /// @{

  unsigned OutputAsmVariant;
  unsigned ShowEncoding : 1;
  unsigned ShowInst : 1;

  /// @}
  /// @name Assembler Options
  /// @{

  unsigned RelaxAll : 1;
  unsigned NoExecStack : 1;

  /// @}

public:
  AssemblerInvocation() {
    Triple = "";
    NoInitialTextSection = 0;
    InputFile = "-";
    OutputPath = "-";
    OutputType = FT_Asm;
    OutputAsmVariant = 0;
    ShowInst = 0;
    ShowEncoding = 0;
    RelaxAll = 0;
    NoExecStack = 0;
  }

  static void CreateFromArgs(AssemblerInvocation &Res, const char **ArgBegin,
                             const char **ArgEnd, Diagnostic &Diags);
};

}

void AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
                                         const char **ArgBegin,
                                         const char **ArgEnd,
                                         Diagnostic &Diags) {
  using namespace clang::driver::cc1asoptions;
  // Parse the arguments.
  OwningPtr<OptTable> OptTbl(createCC1AsOptTable());
  unsigned MissingArgIndex, MissingArgCount;
  OwningPtr<InputArgList> Args(
    OptTbl->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount));

  // Check for missing argument error.
  if (MissingArgCount)
    Diags.Report(diag::err_drv_missing_argument)
      << Args->getArgString(MissingArgIndex) << MissingArgCount;

  // Issue errors on unknown arguments.
  for (arg_iterator it = Args->filtered_begin(cc1asoptions::OPT_UNKNOWN),
         ie = Args->filtered_end(); it != ie; ++it)
    Diags.Report(diag::err_drv_unknown_argument) << (*it) ->getAsString(*Args);

  // Construct the invocation.

  // Target Options
  Opts.Triple = Triple::normalize(Args->getLastArgValue(OPT_triple));
  if (Opts.Triple.empty()) // Use the host triple if unspecified.
    Opts.Triple = sys::getHostTriple();

  // Language Options
  Opts.IncludePaths = Args->getAllArgValues(OPT_I);
  Opts.NoInitialTextSection = Args->hasArg(OPT_n);
  Opts.SaveTemporaryLabels = Args->hasArg(OPT_L);

  // Frontend Options
  if (Args->hasArg(OPT_INPUT)) {
    bool First = true;
    for (arg_iterator it = Args->filtered_begin(OPT_INPUT),
           ie = Args->filtered_end(); it != ie; ++it, First=false) {
      const Arg *A = it;
      if (First)
        Opts.InputFile = A->getValue(*Args);
      else
        Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(*Args);
    }
  }
  Opts.LLVMArgs = Args->getAllArgValues(OPT_mllvm);
  if (Args->hasArg(OPT_fatal_warnings))
    Opts.LLVMArgs.push_back("-fatal-assembler-warnings");
  Opts.OutputPath = Args->getLastArgValue(OPT_o);
  if (Arg *A = Args->getLastArg(OPT_filetype)) {
    StringRef Name = A->getValue(*Args);
    unsigned OutputType = StringSwitch<unsigned>(Name)
      .Case("asm", FT_Asm)
      .Case("null", FT_Null)
      .Case("obj", FT_Obj)
      .Default(~0U);
    if (OutputType == ~0U)
      Diags.Report(diag::err_drv_invalid_value)
        << A->getAsString(*Args) << Name;
    else
      Opts.OutputType = FileType(OutputType);
  }
  Opts.ShowHelp = Args->hasArg(OPT_help);
  Opts.ShowVersion = Args->hasArg(OPT_version);

  // Transliterate Options
  Opts.OutputAsmVariant = Args->getLastArgIntValue(OPT_output_asm_variant,
                                                   0, Diags);
  Opts.ShowEncoding = Args->hasArg(OPT_show_encoding);
  Opts.ShowInst = Args->hasArg(OPT_show_inst);

  // Assemble Options
  Opts.RelaxAll = Args->hasArg(OPT_relax_all);
  Opts.NoExecStack =  Args->hasArg(OPT_no_exec_stack);
}

static formatted_raw_ostream *GetOutputStream(AssemblerInvocation &Opts,
                                              Diagnostic &Diags,
                                              bool Binary) {
  if (Opts.OutputPath.empty())
    Opts.OutputPath = "-";

  // Make sure that the Out file gets unlinked from the disk if we get a
  // SIGINT.
  if (Opts.OutputPath != "-")
    sys::RemoveFileOnSignal(sys::Path(Opts.OutputPath));

  std::string Error;
  raw_fd_ostream *Out =
    new raw_fd_ostream(Opts.OutputPath.c_str(), Error,
                       (Binary ? raw_fd_ostream::F_Binary : 0));
  if (!Error.empty()) {
    Diags.Report(diag::err_fe_unable_to_open_output)
      << Opts.OutputPath << Error;
    return 0;
  }

  return new formatted_raw_ostream(*Out, formatted_raw_ostream::DELETE_STREAM);
}

static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) {
  // Get the target specific parser.
  std::string Error;
  const Target *TheTarget(TargetRegistry::lookupTarget(Opts.Triple, Error));
  if (!TheTarget) {
    Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
    return false;
  }

  OwningPtr<MemoryBuffer> BufferPtr;
  if (error_code ec = MemoryBuffer::getFileOrSTDIN(Opts.InputFile, BufferPtr)) {
    Error = ec.message();
    Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
    return false;
  }
  MemoryBuffer *Buffer = BufferPtr.take();

  SourceMgr SrcMgr;

  // Tell SrcMgr about this buffer, which is what the parser will pick up.
  SrcMgr.AddNewSourceBuffer(Buffer, SMLoc());

  // Record the location of the include directories so that the lexer can find
  // it later.
  SrcMgr.setIncludeDirs(Opts.IncludePaths);

  OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(Opts.Triple));
  assert(MAI && "Unable to create target asm info!");

  OwningPtr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
  assert(MRI && "Unable to create target register info!");

  bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
  formatted_raw_ostream *Out = GetOutputStream(Opts, Diags, IsBinary);
  if (!Out)
    return false;

  // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
  // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
  OwningPtr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
  MCContext Ctx(*MAI, *MRI, MOFI.get());
  // FIXME: Assembler behavior can change with -static.
  MOFI->InitMCObjectFileInfo(Opts.Triple,
                             Reloc::Default, CodeModel::Default, Ctx);
  if (Opts.SaveTemporaryLabels)
    Ctx.setAllowTemporaryLabels(false);

  OwningPtr<MCStreamer> Str;

  OwningPtr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
  OwningPtr<MCSubtargetInfo>
    STI(TheTarget->createMCSubtargetInfo(Opts.Triple, "", ""));

  // FIXME: There is a bit of code duplication with addPassesToEmitFile.
  if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
    MCInstPrinter *IP =
      TheTarget->createMCInstPrinter(Opts.OutputAsmVariant, *MAI, *STI);
    MCCodeEmitter *CE = 0;
    MCAsmBackend *MAB = 0;
    if (Opts.ShowEncoding) {
      CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
      MAB = TheTarget->createMCAsmBackend(Opts.Triple);
    }
    Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true,
                                           /*useLoc*/ true,
                                           /*useCFI*/ true, IP, CE, MAB,
                                           Opts.ShowInst));
  } else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
    Str.reset(createNullStreamer(Ctx));
  } else {
    assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
           "Invalid file type!");
    MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx);
    MCAsmBackend *MAB = TheTarget->createMCAsmBackend(Opts.Triple);
    Str.reset(TheTarget->createMCObjectStreamer(Opts.Triple, Ctx, *MAB, *Out,
                                                CE, Opts.RelaxAll,
                                                Opts.NoExecStack));
    Str.get()->InitSections();
  }

  OwningPtr<MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx,
                                                  *Str.get(), *MAI));
  OwningPtr<MCTargetAsmParser> TAP(TheTarget->createMCAsmParser(*STI, *Parser));
  if (!TAP) {
    Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
    return false;
  }

  Parser->setTargetParser(*TAP.get());

  bool Success = !Parser->Run(Opts.NoInitialTextSection);

  // Close the output.
  delete Out;

  // Delete output on errors.
  if (!Success && Opts.OutputPath != "-")
    sys::Path(Opts.OutputPath).eraseFromDisk();

  return Success;
}

static void LLVMErrorHandler(void *UserData, const std::string &Message) {
  Diagnostic &Diags = *static_cast<Diagnostic*>(UserData);

  Diags.Report(diag::err_fe_error_backend) << Message;

  // We cannot recover from llvm errors.
  exit(1);
}

int cc1as_main(const char **ArgBegin, const char **ArgEnd,
               const char *Argv0, void *MainAddr) {
  // Print a stack trace if we signal out.
  sys::PrintStackTraceOnErrorSignal();
  PrettyStackTraceProgram X(ArgEnd - ArgBegin, ArgBegin);
  llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.

  // Initialize targets and assembly printers/parsers.
  InitializeAllTargetInfos();
  InitializeAllTargetMCs();
  InitializeAllAsmParsers();

  // Construct our diagnostic client.
  TextDiagnosticPrinter *DiagClient
    = new TextDiagnosticPrinter(errs(), DiagnosticOptions());
  DiagClient->setPrefix("clang -cc1as");
  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
  Diagnostic Diags(DiagID, DiagClient);

  // Set an error handler, so that any LLVM backend diagnostics go through our
  // error handler.
  ScopedFatalErrorHandler FatalErrorHandler
    (LLVMErrorHandler, static_cast<void*>(&Diags));

  // Parse the arguments.
  AssemblerInvocation Asm;
  AssemblerInvocation::CreateFromArgs(Asm, ArgBegin, ArgEnd, Diags);

  // Honor -help.
  if (Asm.ShowHelp) {
    llvm::OwningPtr<driver::OptTable> Opts(driver::createCC1AsOptTable());
    Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler");
    return 0;
  }

  // Honor -version.
  //
  // FIXME: Use a better -version message?
  if (Asm.ShowVersion) {
    llvm::cl::PrintVersionMessage();
    return 0;
  }

  // Honor -mllvm.
  //
  // FIXME: Remove this, one day.
  if (!Asm.LLVMArgs.empty()) {
    unsigned NumArgs = Asm.LLVMArgs.size();
    const char **Args = new const char*[NumArgs + 2];
    Args[0] = "clang (LLVM option parsing)";
    for (unsigned i = 0; i != NumArgs; ++i)
      Args[i + 1] = Asm.LLVMArgs[i].c_str();
    Args[NumArgs + 1] = 0;
    llvm::cl::ParseCommandLineOptions(NumArgs + 1, const_cast<char **>(Args));
  }

  // Execute the invocation, unless there were parsing errors.
  bool Success = false;
  if (!Diags.hasErrorOccurred())
    Success = ExecuteAssembler(Asm, Diags);

  // If any timers were active but haven't been destroyed yet, print their
  // results now.
  TimerGroup::printAll(errs());

  return !Success;
}
<|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 "chrome/browser/debugger/devtools_http_protocol_handler.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "base/string_number_conversions.h"
#include "base/thread.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/devtools_messages.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/listen_socket.h"
#include "net/server/http_server_request_info.h"
#include "net/url_request/url_request_context.h"

const int kBufferSize = 16 * 1024;

namespace {

// An internal implementation of DevToolsClientHost that delegates
// messages sent for DevToolsClient to a DebuggerShell instance.
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  explicit DevToolsClientHostImpl(HttpListenSocket* socket)
      : socket_(socket) {}
  ~DevToolsClientHostImpl() {}

  // DevToolsClientHost interface
  virtual void InspectedTabClosing() {
    BrowserThread::PostTask(
        BrowserThread::IO,
        FROM_HERE,
        NewRunnableMethod(socket_,
                          &HttpListenSocket::Close));
  }

  virtual void SendMessageToClient(const IPC::Message& msg) {
    IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
      IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
                          OnDispatchOnInspectorFrontend);
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()
  }

  void NotifyCloseListener() {
    DevToolsClientHost::NotifyCloseListener();
  }
 private:
  // Message handling routines
  void OnDispatchOnInspectorFrontend(const std::string& data) {
    socket_->SendOverWebSocket(data);
  }
  HttpListenSocket* socket_;
};

}

DevToolsHttpProtocolHandler::~DevToolsHttpProtocolHandler() {
  // Stop() must be called prior to this being called
  DCHECK(server_.get() == NULL);
}

void DevToolsHttpProtocolHandler::Start() {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
}

void DevToolsHttpProtocolHandler::Stop() {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
}

void DevToolsHttpProtocolHandler::OnHttpRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  if (info.path == "" || info.path == "/") {
    // Pages discovery request.
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        NewRunnableMethod(this,
                          &DevToolsHttpProtocolHandler::OnHttpRequestUI,
                          make_scoped_refptr(socket),
                          info));
    return;
  }

  size_t pos = info.path.find("/devtools/");
  if (pos != 0) {
    socket->Send404();
    return;
  }

  // Proxy static files from chrome://devtools/*.
  net::URLRequest* request = new net::URLRequest(
      GURL("chrome:/" + info.path), this);
  Bind(request, socket);
  request->set_context(
      Profile::GetDefaultRequestContext()->GetURLRequestContext());
  request->Start();
}

void DevToolsHttpProtocolHandler::OnWebSocketRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
          make_scoped_refptr(socket),
          request));
}

void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket,
                                                     const std::string& data) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
          make_scoped_refptr(socket),
          data));
}

void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) {
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it != socket_to_requests_io_.end()) {
    // Dispose delegating socket.
    for (std::set<net::URLRequest*>::iterator it2 = it->second.begin();
         it2 != it->second.end(); ++it2) {
      net::URLRequest* request = *it2;
      request->Cancel();
      request_to_socket_io_.erase(request);
      request_to_buffer_io_.erase(request);
      delete request;
    }
    socket_to_requests_io_.erase(socket);
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnCloseUI,
          make_scoped_refptr(socket)));
}

void DevToolsHttpProtocolHandler::OnHttpRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  std::string response = "";
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      TabContentsWrapper* tab_contents = model->GetTabContentsAt(i);
      NavigationController& controller = tab_contents->controller();
      NavigationEntry* entry = controller.GetActiveEntry();
      if (entry == NULL)
        continue;

      if (!entry->url().is_valid())
        continue;

      DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
          GetDevToolsClientHostFor(tab_contents->tab_contents()->
                                      render_view_host());
      if (!client_host) {
        response += StringPrintf(
            "%s (%s)
",
            controller.session_id().id(),
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      } else {
        response += StringPrintf(
            "%s (%s)
",
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      }
    }
  }
  response += "";
  Send200(socket, response, "text/html; charset=UTF-8");
}

void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  std::string prefix = "/devtools/page/";
  size_t pos = request.path.find(prefix);
  if (pos != 0) {
    Send404(socket);
    return;
  }
  std::string page_id = request.path.substr(prefix.length());
  int id = 0;
  if (!base::StringToInt(page_id, &id)) {
    Send500(socket, "Invalid page id: " + page_id);
    return;
  }

  TabContents* tab_contents = GetTabContents(id);
  if (tab_contents == NULL) {
    Send500(socket, "No such page id: " + page_id);
    return;
  }

  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
    Send500(socket, "Page with given id is being inspected: " + page_id);
    return;
  }

  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(socket);
  socket_to_client_host_ui_[socket] = client_host;

  manager->RegisterDevToolsClientHostFor(
      tab_contents->render_view_host(),
      client_host);
  AcceptWebSocket(socket, request);
}

void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
    HttpListenSocket* socket,
    const std::string& data) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;

  DevToolsManager* manager = DevToolsManager::GetInstance();

  if (data == "loaded") {
    manager->ForwardToDevToolsAgent(
        it->second,
        DevToolsAgentMsg_FrontendLoaded());
    return;
  }

  manager->ForwardToDevToolsAgent(
      it->second,
      DevToolsAgentMsg_DispatchOnInspectorBackend(data));
}

void DevToolsHttpProtocolHandler::OnCloseUI(HttpListenSocket* socket) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;
  DevToolsClientHostImpl* client_host =
      static_cast<DevToolsClientHostImpl*>(it->second);
  client_host->NotifyCloseListener();
  delete client_host;
  socket_to_client_host_ui_.erase(socket);

  // We are holding last reference to scoped refptr 'socket' here.
  // We can't exit method just like that since 'socket' is going to
  // be destroyed on the UI thread then. Schedule no-op to IO thread
  // so that socket is destroyed on IO instead.
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::ReleaseSocket,
          make_scoped_refptr(socket)));
}

void DevToolsHttpProtocolHandler::OnResponseStarted(net::URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  int expected_size = static_cast<int>(request->GetExpectedContentSize());

  std::string content_type;
  request->GetMimeType(&content_type);

  if (request->status().is_success()) {
    socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n"
                              "Content-Type:%s\r\n"
                              "Content-Length:%d\r\n"
                              "\r\n",
                              content_type.c_str(),
                              expected_size));
  } else {
    socket->Send404();
  }

  int bytes_read = 0;
  // Some servers may treat HEAD requests as GET requests.  To free up the
  // network connection as soon as possible, signal that the request has
  // completed immediately, without trying to read any data back (all we care
  // about is the response code and headers, which we already have).
  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  if (request->status().is_success())
    request->Read(buffer, kBufferSize, &bytes_read);
  OnReadCompleted(request, bytes_read);
}

void DevToolsHttpProtocolHandler::OnReadCompleted(net::URLRequest* request,
                                                  int bytes_read) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  do {
    if (!request->status().is_success() || bytes_read <= 0)
      break;
    socket->Send(buffer->data(), bytes_read);
  } while (request->Read(buffer, kBufferSize, &bytes_read));

  // See comments re: HEAD requests in OnResponseStarted().
  if (!request->status().is_io_pending())
    RequestCompleted(request);
}

DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port)
    : port_(port),
      server_(NULL) {
}

void DevToolsHttpProtocolHandler::Init() {
  server_ = HttpListenSocket::Listen("127.0.0.1", port_, this);
}

// Run on I/O thread
void DevToolsHttpProtocolHandler::Teardown() {
  server_ = NULL;
}

void DevToolsHttpProtocolHandler::Bind(net::URLRequest* request,
                                       HttpListenSocket* socket) {
  request_to_socket_io_[request] = socket;
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it == socket_to_requests_io_.end()) {
    std::pair<HttpListenSocket*, std::set<net::URLRequest*> > value(
        socket,
        std::set<net::URLRequest*>());
    it = socket_to_requests_io_.insert(value).first;
  }
  it->second.insert(request);
  request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
}

void DevToolsHttpProtocolHandler::RequestCompleted(net::URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;
  request_to_socket_io_.erase(request);
  SocketToRequestsMap::iterator it2 = socket_to_requests_io_.find(socket);
  it2->second.erase(request);
  request_to_buffer_io_.erase(request);
  delete request;
}

void DevToolsHttpProtocolHandler::Send200(HttpListenSocket* socket,
                                          const std::string& data,
                                          const std::string& mime_type) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send200,
                        data,
                        mime_type));
}

void DevToolsHttpProtocolHandler::Send404(HttpListenSocket* socket) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send404));
}

void DevToolsHttpProtocolHandler::Send500(HttpListenSocket* socket,
                                          const std::string& message) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send500,
                        message));
}

void DevToolsHttpProtocolHandler::AcceptWebSocket(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::AcceptWebSocket,
                        request));
}

void DevToolsHttpProtocolHandler::ReleaseSocket(
    HttpListenSocket* socket) {
  // This in fact is scoped ref ptr. It'll get nuked on exit.
}

TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      NavigationController& controller =
          model->GetTabContentsAt(i)->controller();
      if (controller.session_id().id() == session_id)
        return controller.tab_contents();
    }
  }
  return NULL;
}
<commit_msg>DevTools: a better fix for socket closure crash.<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 "chrome/browser/debugger/devtools_http_protocol_handler.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "base/string_number_conversions.h"
#include "base/thread.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/devtools_messages.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/listen_socket.h"
#include "net/server/http_server_request_info.h"
#include "net/url_request/url_request_context.h"

const int kBufferSize = 16 * 1024;

namespace {

// An internal implementation of DevToolsClientHost that delegates
// messages sent for DevToolsClient to a DebuggerShell instance.
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  explicit DevToolsClientHostImpl(HttpListenSocket* socket)
      : socket_(socket) {}
  ~DevToolsClientHostImpl() {}

  // DevToolsClientHost interface
  virtual void InspectedTabClosing() {
    BrowserThread::PostTask(
        BrowserThread::IO,
        FROM_HERE,
        NewRunnableMethod(socket_,
                          &HttpListenSocket::Close));
  }

  virtual void SendMessageToClient(const IPC::Message& msg) {
    IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
      IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
                          OnDispatchOnInspectorFrontend);
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()
  }

  void NotifyCloseListener() {
    DevToolsClientHost::NotifyCloseListener();
  }
 private:
  // Message handling routines
  void OnDispatchOnInspectorFrontend(const std::string& data) {
    socket_->SendOverWebSocket(data);
  }
  HttpListenSocket* socket_;
};

}

DevToolsHttpProtocolHandler::~DevToolsHttpProtocolHandler() {
  // Stop() must be called prior to this being called
  DCHECK(server_.get() == NULL);
}

void DevToolsHttpProtocolHandler::Start() {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
}

void DevToolsHttpProtocolHandler::Stop() {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
}

void DevToolsHttpProtocolHandler::OnHttpRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  if (info.path == "" || info.path == "/") {
    // Pages discovery request.
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        NewRunnableMethod(this,
                          &DevToolsHttpProtocolHandler::OnHttpRequestUI,
                          make_scoped_refptr(socket),
                          info));
    return;
  }

  size_t pos = info.path.find("/devtools/");
  if (pos != 0) {
    socket->Send404();
    return;
  }

  // Proxy static files from chrome://devtools/*.
  net::URLRequest* request = new net::URLRequest(
      GURL("chrome:/" + info.path), this);
  Bind(request, socket);
  request->set_context(
      Profile::GetDefaultRequestContext()->GetURLRequestContext());
  request->Start();
}

void DevToolsHttpProtocolHandler::OnWebSocketRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
          make_scoped_refptr(socket),
          request));
}

void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket,
                                                     const std::string& data) {
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
          make_scoped_refptr(socket),
          data));
}

void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) {
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it != socket_to_requests_io_.end()) {
    // Dispose delegating socket.
    for (std::set<net::URLRequest*>::iterator it2 = it->second.begin();
         it2 != it->second.end(); ++it2) {
      net::URLRequest* request = *it2;
      request->Cancel();
      request_to_socket_io_.erase(request);
      request_to_buffer_io_.erase(request);
      delete request;
    }
    socket_to_requests_io_.erase(socket);
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnCloseUI,
          make_scoped_refptr(socket)));
}

void DevToolsHttpProtocolHandler::OnHttpRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  std::string response = "";
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      TabContentsWrapper* tab_contents = model->GetTabContentsAt(i);
      NavigationController& controller = tab_contents->controller();
      NavigationEntry* entry = controller.GetActiveEntry();
      if (entry == NULL)
        continue;

      if (!entry->url().is_valid())
        continue;

      DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
          GetDevToolsClientHostFor(tab_contents->tab_contents()->
                                      render_view_host());
      if (!client_host) {
        response += StringPrintf(
            "%s (%s)
",
            controller.session_id().id(),
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      } else {
        response += StringPrintf(
            "%s (%s)
",
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      }
    }
  }
  response += "";
  Send200(socket, response, "text/html; charset=UTF-8");
}

void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  std::string prefix = "/devtools/page/";
  size_t pos = request.path.find(prefix);
  if (pos != 0) {
    Send404(socket);
    return;
  }
  std::string page_id = request.path.substr(prefix.length());
  int id = 0;
  if (!base::StringToInt(page_id, &id)) {
    Send500(socket, "Invalid page id: " + page_id);
    return;
  }

  TabContents* tab_contents = GetTabContents(id);
  if (tab_contents == NULL) {
    Send500(socket, "No such page id: " + page_id);
    return;
  }

  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
    Send500(socket, "Page with given id is being inspected: " + page_id);
    return;
  }

  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(socket);
  socket_to_client_host_ui_[socket] = client_host;

  manager->RegisterDevToolsClientHostFor(
      tab_contents->render_view_host(),
      client_host);
  AcceptWebSocket(socket, request);
}

void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
    HttpListenSocket* socket,
    const std::string& data) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;

  DevToolsManager* manager = DevToolsManager::GetInstance();

  if (data == "loaded") {
    manager->ForwardToDevToolsAgent(
        it->second,
        DevToolsAgentMsg_FrontendLoaded());
    return;
  }

  manager->ForwardToDevToolsAgent(
      it->second,
      DevToolsAgentMsg_DispatchOnInspectorBackend(data));
}

void DevToolsHttpProtocolHandler::OnCloseUI(HttpListenSocket* socket) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it != socket_to_client_host_ui_.end()) {
    DevToolsClientHostImpl* client_host =
        static_cast<DevToolsClientHostImpl*>(it->second);
    client_host->NotifyCloseListener();
    delete client_host;
    socket_to_client_host_ui_.erase(socket);
  }

  // We are holding last reference to scoped refptr 'socket' here.
  // We can't exit method just like that since 'socket' is going to
  // be destroyed on the UI thread then. Schedule no-op to IO thread
  // so that socket is destroyed on IO instead.
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::ReleaseSocket,
          make_scoped_refptr(socket)));
}

void DevToolsHttpProtocolHandler::OnResponseStarted(net::URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  int expected_size = static_cast<int>(request->GetExpectedContentSize());

  std::string content_type;
  request->GetMimeType(&content_type);

  if (request->status().is_success()) {
    socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n"
                              "Content-Type:%s\r\n"
                              "Content-Length:%d\r\n"
                              "\r\n",
                              content_type.c_str(),
                              expected_size));
  } else {
    socket->Send404();
  }

  int bytes_read = 0;
  // Some servers may treat HEAD requests as GET requests.  To free up the
  // network connection as soon as possible, signal that the request has
  // completed immediately, without trying to read any data back (all we care
  // about is the response code and headers, which we already have).
  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  if (request->status().is_success())
    request->Read(buffer, kBufferSize, &bytes_read);
  OnReadCompleted(request, bytes_read);
}

void DevToolsHttpProtocolHandler::OnReadCompleted(net::URLRequest* request,
                                                  int bytes_read) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  do {
    if (!request->status().is_success() || bytes_read <= 0)
      break;
    socket->Send(buffer->data(), bytes_read);
  } while (request->Read(buffer, kBufferSize, &bytes_read));

  // See comments re: HEAD requests in OnResponseStarted().
  if (!request->status().is_io_pending())
    RequestCompleted(request);
}

DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port)
    : port_(port),
      server_(NULL) {
}

void DevToolsHttpProtocolHandler::Init() {
  server_ = HttpListenSocket::Listen("127.0.0.1", port_, this);
}

// Run on I/O thread
void DevToolsHttpProtocolHandler::Teardown() {
  server_ = NULL;
}

void DevToolsHttpProtocolHandler::Bind(net::URLRequest* request,
                                       HttpListenSocket* socket) {
  request_to_socket_io_[request] = socket;
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it == socket_to_requests_io_.end()) {
    std::pair<HttpListenSocket*, std::set<net::URLRequest*> > value(
        socket,
        std::set<net::URLRequest*>());
    it = socket_to_requests_io_.insert(value).first;
  }
  it->second.insert(request);
  request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
}

void DevToolsHttpProtocolHandler::RequestCompleted(net::URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;
  request_to_socket_io_.erase(request);
  SocketToRequestsMap::iterator it2 = socket_to_requests_io_.find(socket);
  it2->second.erase(request);
  request_to_buffer_io_.erase(request);
  delete request;
}

void DevToolsHttpProtocolHandler::Send200(HttpListenSocket* socket,
                                          const std::string& data,
                                          const std::string& mime_type) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send200,
                        data,
                        mime_type));
}

void DevToolsHttpProtocolHandler::Send404(HttpListenSocket* socket) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send404));
}

void DevToolsHttpProtocolHandler::Send500(HttpListenSocket* socket,
                                          const std::string& message) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send500,
                        message));
}

void DevToolsHttpProtocolHandler::AcceptWebSocket(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::AcceptWebSocket,
                        request));
}

void DevToolsHttpProtocolHandler::ReleaseSocket(
    HttpListenSocket* socket) {
  // This in fact is scoped ref ptr. It'll get nuked on exit.
}

TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      NavigationController& controller =
          model->GetTabContentsAt(i)->controller();
      if (controller.session_id().id() == session_id)
        return controller.tab_contents();
    }
  }
  return NULL;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Patrizio Bekerle -- http://www.bekerle.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 */

#include "qtexteditsearchwidget.h"
#include "ui_qtexteditsearchwidget.h"
#include <QEvent>
#include <QKeyEvent>
#include <QDebug>

QTextEditSearchWidget::QTextEditSearchWidget(QTextEdit *parent) :
    QWidget(parent),
    ui(new Ui::QTextEditSearchWidget)
{
    ui->setupUi(this);
    _textEdit = parent;
    hide();

    QObject::connect(ui->closeButton, SIGNAL(clicked()),
                     this, SLOT(deactivate()));
    QObject::connect(ui->searchLineEdit, SIGNAL(textChanged(const QString &)),
                     this, SLOT(searchLineEditTextChanged(const QString &)));
    QObject::connect(ui->searchDownButton, SIGNAL(clicked()),
                     this, SLOT(doSearchDown()));
    QObject::connect(ui->searchUpButton, SIGNAL(clicked()),
                     this, SLOT(doSearchUp()));
    QObject::connect(ui->replaceToggleButton, SIGNAL(toggled(bool)),
                     this, SLOT(setReplaceMode(bool)));
    QObject::connect(ui->replaceButton, SIGNAL(clicked()),
                     this, SLOT(doReplace()));
    QObject::connect(ui->replaceAllButton, SIGNAL(clicked()),
                     this, SLOT(doReplaceAll()));

    installEventFilter(this);
    ui->searchLineEdit->installEventFilter(this);
    ui->replaceLineEdit->installEventFilter(this);

#ifdef Q_OS_MAC
    // set the spacing to 8 for OS X
    layout()->setSpacing(8);
    ui->buttonFrame->layout()->setSpacing(9);

    // set the margin to 0 for the top buttons for OS X
    QString buttonStyle = "QPushButton {margin: 0}";
    ui->closeButton->setStyleSheet(buttonStyle);
    ui->searchDownButton->setStyleSheet(buttonStyle);
    ui->searchUpButton->setStyleSheet(buttonStyle);
    ui->replaceToggleButton->setStyleSheet(buttonStyle);
    ui->matchCaseSensitiveButton->setStyleSheet(buttonStyle);
#endif
}

QTextEditSearchWidget::~QTextEditSearchWidget() {
    delete ui;
}

void QTextEditSearchWidget::activate() {
    setReplaceMode(false);
    show();
    ui->searchLineEdit->setFocus();
    ui->searchLineEdit->selectAll();
    doSearchDown();
}

void QTextEditSearchWidget::activateReplace() {
    // replacing is prohibited if the text edit is readonly
    if (_textEdit->isReadOnly()) {
        return;
    }

    ui->searchLineEdit->setText(_textEdit->textCursor().selectedText());
    ui->searchLineEdit->selectAll();
    activate();
    setReplaceMode(true);
}

void QTextEditSearchWidget::deactivate() {
    hide();
    _textEdit->setFocus();
}

void QTextEditSearchWidget::setReplaceMode(bool enabled) {
    ui->replaceToggleButton->setChecked(enabled);
    ui->replaceLabel->setVisible(enabled);
    ui->replaceLineEdit->setVisible(enabled);
    ui->modeLabel->setVisible(enabled);
    ui->buttonFrame->setVisible(enabled);
    ui->matchCaseSensitiveButton->setVisible(enabled);
}

bool QTextEditSearchWidget::eventFilter(QObject *obj, QEvent *event) {
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);

        if (keyEvent->key() == Qt::Key_Escape) {
            deactivate();
            return true;
        } else if ((keyEvent->modifiers().testFlag(Qt::ShiftModifier) &&
                  (keyEvent->key() == Qt::Key_Return)) ||
                 (keyEvent->key() == Qt::Key_Up)) {
            doSearchUp();
            return true;
        } else if ((keyEvent->key() == Qt::Key_Return) ||
                 (keyEvent->key() == Qt::Key_Down)) {
            doSearchDown();
            return true;
        } else if (keyEvent->key() == Qt::Key_F3) {
            doSearch(!keyEvent->modifiers().testFlag(Qt::ShiftModifier));
            return true;
        }

//        if ((obj == ui->replaceLineEdit) && (keyEvent->key() == Qt::Key_Tab)
//                && ui->replaceToggleButton->isChecked()) {
//            ui->replaceLineEdit->setFocus();
//        }

        return false;
    }

    return QWidget::eventFilter(obj, event);
}

void QTextEditSearchWidget::searchLineEditTextChanged(const QString &arg1) {
    Q_UNUSED(arg1);
    doSearchDown();
}

void QTextEditSearchWidget::doSearchUp() {
    doSearch(false);
}

void QTextEditSearchWidget::doSearchDown() {
    doSearch(true);
}

bool QTextEditSearchWidget::doReplace(bool forAll) {
    if (_textEdit->isReadOnly()) {
        return false;
    }

    QTextCursor c = _textEdit->textCursor();

    if (!forAll && c.selectedText().isEmpty()) {
        return false;
    }

    int searchMode = ui->modeComboBox->currentIndex();
    if (searchMode == RegularExpressionMode) {
        QString text = c.selectedText();
        text.replace(QRegExp(ui->searchLineEdit->text()),
                             ui->replaceLineEdit->text());
        c.insertText(text);
    } else {
        c.insertText(ui->replaceLineEdit->text());
    }

    if (!forAll) {
        int position = c.position();

        if (!doSearch(true)) {
            // restore the last cursor position if text wasn't found any more
            c.setPosition(position);
            _textEdit->setTextCursor(c);
        }
    }

    return true;
}

void QTextEditSearchWidget::doReplaceAll() {
    if (_textEdit->isReadOnly()) {
        return;
    }

    // start at the top
    _textEdit->moveCursor(QTextCursor::Start);

    // replace until everything to the bottom is replaced
    while (doSearch(true, false) && doReplace(true)) {}
}

/**
 * @brief Searches for text in the text edit
 * @returns true if found
 */
bool QTextEditSearchWidget::doSearch(bool searchDown, bool allowRestartAtTop) {
    QString text = ui->searchLineEdit->text();

    if (text == "") {
        ui->searchLineEdit->setStyleSheet("* { background: none; }");
        return false;
    }

    int searchMode = ui->modeComboBox->currentIndex();

    QFlags<QTextDocument::FindFlag> options = searchDown ?
                                              QTextDocument::FindFlag(0)
                                              : QTextDocument::FindBackward;
    if (searchMode == WholeWordsMode) {
        options |= QTextDocument::FindWholeWords;
    }

    if (ui->matchCaseSensitiveButton->isChecked()) {
        options |= QTextDocument::FindCaseSensitively;
    }

    bool found;
    if (searchMode == RegularExpressionMode) {
        found = _textEdit->find(QRegExp(text), options);
    } else {
        found = _textEdit->find(text, options);
    }

    // start at the top (or bottom) if not found
    if (!found && allowRestartAtTop) {
        _textEdit->moveCursor(
                searchDown ? QTextCursor::Start : QTextCursor::End);
        found = _textEdit->find(text, options);
    }

    QRect rect = _textEdit->cursorRect();
    QMargins margins = _textEdit->layout()->contentsMargins();
    int searchWidgetHotArea = _textEdit->height() - this->height();
    int marginBottom = (rect.y() > searchWidgetHotArea) ? (this->height() + 10)
                                                        : 0;

    // move the search box a bit up if we would block the search result
    if (margins.bottom() != marginBottom) {
        margins.setBottom(marginBottom);
        _textEdit->layout()->setContentsMargins(margins);
    }

    // add a background color according if we found the text or not
    QString colorCode = found ? "#D5FAE2" : "#FAE9EB";
    ui->searchLineEdit->setStyleSheet("* { background: " + colorCode + "; }");

    return found;
}

<commit_msg>when doing a text search in your note the selected text is now preset as search text if there is any and there is no other search text filled in<commit_after>/*
 * Copyright (C) 2016 Patrizio Bekerle -- http://www.bekerle.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 */

#include "qtexteditsearchwidget.h"
#include "ui_qtexteditsearchwidget.h"
#include <QEvent>
#include <QKeyEvent>
#include <QDebug>

QTextEditSearchWidget::QTextEditSearchWidget(QTextEdit *parent) :
    QWidget(parent),
    ui(new Ui::QTextEditSearchWidget)
{
    ui->setupUi(this);
    _textEdit = parent;
    hide();

    QObject::connect(ui->closeButton, SIGNAL(clicked()),
                     this, SLOT(deactivate()));
    QObject::connect(ui->searchLineEdit, SIGNAL(textChanged(const QString &)),
                     this, SLOT(searchLineEditTextChanged(const QString &)));
    QObject::connect(ui->searchDownButton, SIGNAL(clicked()),
                     this, SLOT(doSearchDown()));
    QObject::connect(ui->searchUpButton, SIGNAL(clicked()),
                     this, SLOT(doSearchUp()));
    QObject::connect(ui->replaceToggleButton, SIGNAL(toggled(bool)),
                     this, SLOT(setReplaceMode(bool)));
    QObject::connect(ui->replaceButton, SIGNAL(clicked()),
                     this, SLOT(doReplace()));
    QObject::connect(ui->replaceAllButton, SIGNAL(clicked()),
                     this, SLOT(doReplaceAll()));

    installEventFilter(this);
    ui->searchLineEdit->installEventFilter(this);
    ui->replaceLineEdit->installEventFilter(this);

#ifdef Q_OS_MAC
    // set the spacing to 8 for OS X
    layout()->setSpacing(8);
    ui->buttonFrame->layout()->setSpacing(9);

    // set the margin to 0 for the top buttons for OS X
    QString buttonStyle = "QPushButton {margin: 0}";
    ui->closeButton->setStyleSheet(buttonStyle);
    ui->searchDownButton->setStyleSheet(buttonStyle);
    ui->searchUpButton->setStyleSheet(buttonStyle);
    ui->replaceToggleButton->setStyleSheet(buttonStyle);
    ui->matchCaseSensitiveButton->setStyleSheet(buttonStyle);
#endif
}

QTextEditSearchWidget::~QTextEditSearchWidget() {
    delete ui;
}

void QTextEditSearchWidget::activate() {
    setReplaceMode(false);
    show();

    // preset the selected text as search text if there is any and there is no
    // other search text
    QString selectedText = _textEdit->textCursor().selectedText();
    if (!selectedText.isEmpty() && ui->searchLineEdit->text().isEmpty()) {
        ui->searchLineEdit->setText(selectedText);
    }

    ui->searchLineEdit->setFocus();
    ui->searchLineEdit->selectAll();
    doSearchDown();
}

void QTextEditSearchWidget::activateReplace() {
    // replacing is prohibited if the text edit is readonly
    if (_textEdit->isReadOnly()) {
        return;
    }

    ui->searchLineEdit->setText(_textEdit->textCursor().selectedText());
    ui->searchLineEdit->selectAll();
    activate();
    setReplaceMode(true);
}

void QTextEditSearchWidget::deactivate() {
    hide();
    _textEdit->setFocus();
}

void QTextEditSearchWidget::setReplaceMode(bool enabled) {
    ui->replaceToggleButton->setChecked(enabled);
    ui->replaceLabel->setVisible(enabled);
    ui->replaceLineEdit->setVisible(enabled);
    ui->modeLabel->setVisible(enabled);
    ui->buttonFrame->setVisible(enabled);
    ui->matchCaseSensitiveButton->setVisible(enabled);
}

bool QTextEditSearchWidget::eventFilter(QObject *obj, QEvent *event) {
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);

        if (keyEvent->key() == Qt::Key_Escape) {
            deactivate();
            return true;
        } else if ((keyEvent->modifiers().testFlag(Qt::ShiftModifier) &&
                  (keyEvent->key() == Qt::Key_Return)) ||
                 (keyEvent->key() == Qt::Key_Up)) {
            doSearchUp();
            return true;
        } else if ((keyEvent->key() == Qt::Key_Return) ||
                 (keyEvent->key() == Qt::Key_Down)) {
            doSearchDown();
            return true;
        } else if (keyEvent->key() == Qt::Key_F3) {
            doSearch(!keyEvent->modifiers().testFlag(Qt::ShiftModifier));
            return true;
        }

//        if ((obj == ui->replaceLineEdit) && (keyEvent->key() == Qt::Key_Tab)
//                && ui->replaceToggleButton->isChecked()) {
//            ui->replaceLineEdit->setFocus();
//        }

        return false;
    }

    return QWidget::eventFilter(obj, event);
}

void QTextEditSearchWidget::searchLineEditTextChanged(const QString &arg1) {
    Q_UNUSED(arg1);
    doSearchDown();
}

void QTextEditSearchWidget::doSearchUp() {
    doSearch(false);
}

void QTextEditSearchWidget::doSearchDown() {
    doSearch(true);
}

bool QTextEditSearchWidget::doReplace(bool forAll) {
    if (_textEdit->isReadOnly()) {
        return false;
    }

    QTextCursor c = _textEdit->textCursor();

    if (!forAll && c.selectedText().isEmpty()) {
        return false;
    }

    int searchMode = ui->modeComboBox->currentIndex();
    if (searchMode == RegularExpressionMode) {
        QString text = c.selectedText();
        text.replace(QRegExp(ui->searchLineEdit->text()),
                             ui->replaceLineEdit->text());
        c.insertText(text);
    } else {
        c.insertText(ui->replaceLineEdit->text());
    }

    if (!forAll) {
        int position = c.position();

        if (!doSearch(true)) {
            // restore the last cursor position if text wasn't found any more
            c.setPosition(position);
            _textEdit->setTextCursor(c);
        }
    }

    return true;
}

void QTextEditSearchWidget::doReplaceAll() {
    if (_textEdit->isReadOnly()) {
        return;
    }

    // start at the top
    _textEdit->moveCursor(QTextCursor::Start);

    // replace until everything to the bottom is replaced
    while (doSearch(true, false) && doReplace(true)) {}
}

/**
 * @brief Searches for text in the text edit
 * @returns true if found
 */
bool QTextEditSearchWidget::doSearch(bool searchDown, bool allowRestartAtTop) {
    QString text = ui->searchLineEdit->text();

    if (text == "") {
        ui->searchLineEdit->setStyleSheet("* { background: none; }");
        return false;
    }

    int searchMode = ui->modeComboBox->currentIndex();

    QFlags<QTextDocument::FindFlag> options = searchDown ?
                                              QTextDocument::FindFlag(0)
                                              : QTextDocument::FindBackward;
    if (searchMode == WholeWordsMode) {
        options |= QTextDocument::FindWholeWords;
    }

    if (ui->matchCaseSensitiveButton->isChecked()) {
        options |= QTextDocument::FindCaseSensitively;
    }

    bool found;
    if (searchMode == RegularExpressionMode) {
        found = _textEdit->find(QRegExp(text), options);
    } else {
        found = _textEdit->find(text, options);
    }

    // start at the top (or bottom) if not found
    if (!found && allowRestartAtTop) {
        _textEdit->moveCursor(
                searchDown ? QTextCursor::Start : QTextCursor::End);
        found = _textEdit->find(text, options);
    }

    QRect rect = _textEdit->cursorRect();
    QMargins margins = _textEdit->layout()->contentsMargins();
    int searchWidgetHotArea = _textEdit->height() - this->height();
    int marginBottom = (rect.y() > searchWidgetHotArea) ? (this->height() + 10)
                                                        : 0;

    // move the search box a bit up if we would block the search result
    if (margins.bottom() != marginBottom) {
        margins.setBottom(marginBottom);
        _textEdit->layout()->setContentsMargins(margins);
    }

    // add a background color according if we found the text or not
    QString colorCode = found ? "#D5FAE2" : "#FAE9EB";
    ui->searchLineEdit->setStyleSheet("* { background: " + colorCode + "; }");

    return found;
}

<|endoftext|>


<commit_before>#include "disassembler.h"
#include "util.h"
#include "symtab.h"

#include <vector>
#include <cctype>

using namespace std;

enum ByteTypeGuess {
	UNINITIALIZED,
	UNKNOWN,
	CHAR_DATA,
	UNINITIALIZED_CHAR_DATA,
	WORD_DATA,
	UNINITIALIZED_WORD_DATA,
	CODE,
};

struct program {
	ByteTypeGuess byte_type_guess[65536];
	char memory[65536];
	bool is_labelled[65536];

	string name;
	string starting_address;
	int length_of_program;
	string first_executable_instruction;
	program() {
		for ( int i = 0 ; i < 65536 ; i++ ) {
			byte_type_guess[i] = UNINITIALIZED;
			memory[i] = 0;
			is_labelled[i] = false;
		}
		name = "";
		starting_address = "";
		length_of_program = 0;
		first_executable_instruction = "";
	}
};

bool read_record(ifstream &ifile, string &record);
void record_to_memory(const string record, program &p);
void analyze_code_data(program &p);
void write_assembly(const program &p, ofstream &ofile);

bool disassemble(ifstream &ifile, ofstream &ofile) {
	string record;
	program p;
	while ( read_record(ifile, record) ) {
		record_to_memory(record, p);
	}
	status("Done reading records");
	analyze_code_data(p);
	status("Done analyzing program for code and data");
	write_assembly(p, ofile);
	status("Done writing output file");
	return true;
}

string read_columns(ifstream &ifile, int col_begin, int col_end, char record_type = 'a') { // inclusive of both
	string ret;
	char t;
	for ( int col = col_begin ; col <= col_end ; col++ ) {
		t = ifile.get();
		if ( t == EOF || t == '\n' ) {
			string errstr;
			errstr += "Unexpected end of ";
			errstr += record_type;
			errstr += " record";
			fatal(errstr);
		}
		ret += t;
	}
	return ret;
}

string read_hex_columns(ifstream &ifile, int col_begin, int col_end, char record_type = 'a') { // inclusive of both
	string ret;
	ret = read_columns(ifile,col_begin,col_end,record_type);
	make_upper_case(ret);
	if ( !is_hex_string(ret) ) {
		string errstr;
		errstr += "Unexpected non-hexadecimal character found in ";
		errstr += record_type;
		errstr += " record";
		fatal(errstr);
	}
	return ret;
}

bool read_record(ifstream &ifile, string &record) {
	int temp_int;
	string temp_str;
	char t;

	do {
		t = ifile.get();
		if ( t == EOF ) {
			return false;
		}
	} while ( t == '\n' || t == ' ' );
	record = "";
	record += t;

	switch (t) {
		case 'H': // Header
			record += read_columns(ifile,2,7,'H');
			record += read_hex_columns(ifile,8,19,'H');
			break;
		case 'T': // Text
			record += read_hex_columns(ifile,2,7,'T');
			temp_str = read_hex_columns(ifile,8,9,'T');
			temp_int = hex2int(temp_str);
			record += temp_str;
			record +=  read_hex_columns(ifile,10,9+2*temp_int,'T');
			break;
		case 'E': // End
			record += read_hex_columns(ifile,2,7,'E');
			break;
		default:
			{
				string errstr = "Unknown record type ";
				errstr += t;
				fatal(errstr);
			}
	}
	return true;
}

void record_to_memory(const string record, program &p) {
	const char * c_record = record.c_str();
	switch (*c_record) {
		case 'H': // Header
			if ( p.name.length() != 0 ) {
				fatal("Multiple H records");
			}
			p.name = record.substr(1,6);
			p.starting_address = record.substr(7,6);
			p.length_of_program = hex2int(record.substr(13,6));
			break;
		case 'T': // Text
			{
				int text_start = hex2int(record.substr(1,6));
				int bit_length = hex2int(record.substr(7,2));
				for ( int i = 0 ; i < bit_length ; i++ ) {
					p.memory[i+text_start] = hex2int(record.substr(9+2*i,2));
					p.byte_type_guess[i+text_start] = UNKNOWN;
				}
			}
			break;
		case 'E': // End
			if ( p.first_executable_instruction.length() != 0 ) {
				fatal("Multiple E records");
			}
			p.first_executable_instruction = record.substr(1,6);
			break;
		default:
			{
				string errstr;
				errstr += "Unknown record type ";
				errstr += *c_record;
				fatal(errstr);
			}
	}
}

void mark_code_data(program &p, int location, ByteTypeGuess btg) {
	if ( location >= hex2int(p.starting_address) + p.length_of_program ) {
		return;
	}
	switch (btg) {
		case UNINITIALIZED:
		case UNKNOWN:
			break;
		case CHAR_DATA:
		case UNINITIALIZED_CHAR_DATA:
			{
				if ( p.byte_type_guess[location] != UNKNOWN &&
					 p.byte_type_guess[location] != UNINITIALIZED ) {
					break;
				}
				if ( p.byte_type_guess[location] == UNINITIALIZED )
					btg = UNINITIALIZED_CHAR_DATA;
				p.byte_type_guess[location] = btg;
				break;
			}
		case WORD_DATA:
		case UNINITIALIZED_WORD_DATA:
			{
				if ( p.byte_type_guess[location] != UNKNOWN &&
					 p.byte_type_guess[location] != UNINITIALIZED ) {
					break;
				}
				if ( p.byte_type_guess[location] == UNINITIALIZED )
					btg = UNINITIALIZED_WORD_DATA;
				p.byte_type_guess[location+0] = btg;
				p.byte_type_guess[location+1] = btg;
				p.byte_type_guess[location+2] = btg;
				break;
			}
		case CODE:
			{
				if ( p.byte_type_guess[location] == UNINITIALIZED ) {
					fatal("Attempting to use uninitialized section as code");
				}
				if ( p.byte_type_guess[location] == CODE ) {
					break;
				}
				p.byte_type_guess[location+0] = btg;
				p.byte_type_guess[location+1] = btg;
				p.byte_type_guess[location+2] = btg;

				string opcode_val = byte2hex(p.memory[location]);
				string opcode;
				string operand = byte2hex(p.memory[location+1])+byte2hex(p.memory[location+2]);
				operand = int2hex(hex2int(operand)&0x7FFF); // remove index flag
				if ( ! find_from_symtab(opcode,opcode_val) ) {
					string errstr;
					errstr += "Unknown opcode ";
					errstr += opcode_val;
					errstr += " at location ";
					errstr += int2hex(location);
					fatal(errstr);
				}

				if ( opcode == "ADD"  ||
					 opcode == "AND"  ||
					 opcode == "COMP" ||
					 opcode == "DIV"  ||
					 opcode == "LDA"  ||
					 opcode == "LDL"  ||
					 opcode == "LDX"  ||
					 opcode == "MUL"  ||
					 opcode == "OR"   ||
					 opcode == "STA"  ||
					 opcode == "STL"  ||
					 opcode == "STX"  ||
					 opcode == "SUB"  ||
					 opcode == "TIX" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),WORD_DATA);
				}

				if ( opcode == "LDCH" ||
					 opcode == "STCH" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),CHAR_DATA);
				}

				if ( opcode == "J"    ||
					 opcode == "JEQ"  ||
					 opcode == "JGT"  ||
					 opcode == "JLT"  ||
					 opcode == "JSUB" ||
					 opcode == "RSUB" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),CODE);
				}

				if ( opcode != "J" &&
					 opcode != "RSUB" ) {
					mark_code_data(p,location+3,btg);
				}

				break;
			}
	}
}

void analyze_code_data(program &p) {
	if ( p.name.length() == 0 ) {
		fatal("No Header record");
	}
	if ( p.first_executable_instruction.length() == 0 ) {
		fatal("No End record");
	}
	initialize_symtab();
	add_to_symtab("FIRST",p.first_executable_instruction);
	p.is_labelled[hex2int(p.first_executable_instruction)] = true;
	mark_code_data(p,hex2int(p.first_executable_instruction),CODE);
}

string asm_to_line(string label, string opcode, string operand, bool is_indexed) {
	const int labellength = 8;
	const int opcodelength = 6;
	string ret = "";
	ret += label + string(labellength-label.length(),' ');
	ret += opcode + string(opcodelength-opcode.length(),' ');
	ret += operand;
	if ( is_indexed ) {
		ret += ",X";
	}
	ret += '\n';
	return ret;
}

void write_assembly(const program &p, ofstream &ofile) {
	ofile << asm_to_line(p.name,"START",p.starting_address,false);
	int start_of_program = hex2int(p.starting_address);
	int end_of_program = start_of_program + p.length_of_program;

	for ( int locctr = start_of_program ; locctr < end_of_program ; ) {
		string label = "";
		string opcode = "";
		string operand = "";
		bool is_indexed = false;

		if ( p.byte_type_guess[locctr] == CODE ) {
			if ( ! find_from_symtab(opcode,byte2hex(p.memory[locctr])) ) {
				string errstr;
				errstr += "Unknown opcode ";
				errstr += opcode;
				errstr += " at location ";
				errstr += int2hex(locctr);
				fatal(errstr);
			}
			operand = byte2hex(p.memory[locctr+1])+byte2hex(p.memory[locctr+2]);
			is_indexed = (hex2int(operand)&0x8000);
			operand = int2hex(hex2int(operand)&0x7FFF); // remove index flag

			if ( opcode != "RD" &&
				 opcode != "TD" &&
				 opcode != "WD" &&
				 opcode != "RSUB" ) {
				if ( !find_from_symtab(operand,operand) ) {
					string errstr;
					errstr += "Label not created for operand ";
					errstr += operand;
					error(errstr);
				}
			}

			if ( opcode == "RSUB" ) {
				operand = "";
			}

			label = "";
			if ( p.is_labelled[locctr] ) {
				if ( !find_from_symtab(label,int2hex(locctr)) ) {
					string errstr;
					errstr += "Label not created for location ";
					errstr += int2hex(locctr);
					error(errstr);
				}
			} // TODO: Refactor to prevent rewrite of code

			locctr += 3;
		} else if ( p.byte_type_guess[locctr] == CHAR_DATA ) {
			if ( p.is_labelled[locctr] ) {
				if ( !find_from_symtab(label,int2hex(locctr)) ) {
					string errstr;
					errstr += "Label not created for location ";
					errstr += int2hex(locctr);
					error(errstr);
				}
			} // TODO: Refactor to prevent rewrite of code

			vector<char> byte_list;
			bool type_c = true;
			do {
				byte_list.push_back(p.memory[locctr]);
				if ( !isprint(p.memory[locctr]) ) {
					type_c = false;
				}
				locctr++;
			} while ( p.byte_type_guess[locctr] == CHAR_DATA && !p.is_labelled[locctr] );

			opcode = "CHAR";

			operand += (type_c?"C":"X");
			operand += "'";
			for ( int i = 0 ; i < (int)byte_list.size() ; i++ ) {
				if ( type_c ) {
					operand += byte_list[i];
				} else {
					operand += byte2hex(byte_list[i]);
				}
			}
			operand += "'";
		} else {
			// TODO
			locctr++; // temporarily
		}
		ofile << asm_to_line(label,opcode,operand,is_indexed);
	}
	ofile << asm_to_line("","END","FIRST",false);
}<commit_msg>Refactor<commit_after>#include "disassembler.h"
#include "util.h"
#include "symtab.h"

#include <vector>
#include <cctype>

using namespace std;

enum ByteTypeGuess {
	UNINITIALIZED,
	UNKNOWN,
	CHAR_DATA,
	UNINITIALIZED_CHAR_DATA,
	WORD_DATA,
	UNINITIALIZED_WORD_DATA,
	CODE,
};

struct program {
	ByteTypeGuess byte_type_guess[65536];
	char memory[65536];
	bool is_labelled[65536];

	string name;
	string starting_address;
	int length_of_program;
	string first_executable_instruction;
	program() {
		for ( int i = 0 ; i < 65536 ; i++ ) {
			byte_type_guess[i] = UNINITIALIZED;
			memory[i] = 0;
			is_labelled[i] = false;
		}
		name = "";
		starting_address = "";
		length_of_program = 0;
		first_executable_instruction = "";
	}
};

bool read_record(ifstream &ifile, string &record);
void record_to_memory(const string record, program &p);
void analyze_code_data(program &p);
void write_assembly(const program &p, ofstream &ofile);

bool disassemble(ifstream &ifile, ofstream &ofile) {
	string record;
	program p;
	while ( read_record(ifile, record) ) {
		record_to_memory(record, p);
	}
	status("Done reading records");
	analyze_code_data(p);
	status("Done analyzing program for code and data");
	write_assembly(p, ofile);
	status("Done writing output file");
	return true;
}

string read_columns(ifstream &ifile, int col_begin, int col_end, char record_type = 'a') { // inclusive of both
	string ret;
	char t;
	for ( int col = col_begin ; col <= col_end ; col++ ) {
		t = ifile.get();
		if ( t == EOF || t == '\n' ) {
			string errstr;
			errstr += "Unexpected end of ";
			errstr += record_type;
			errstr += " record";
			fatal(errstr);
		}
		ret += t;
	}
	return ret;
}

string read_hex_columns(ifstream &ifile, int col_begin, int col_end, char record_type = 'a') { // inclusive of both
	string ret;
	ret = read_columns(ifile,col_begin,col_end,record_type);
	make_upper_case(ret);
	if ( !is_hex_string(ret) ) {
		string errstr;
		errstr += "Unexpected non-hexadecimal character found in ";
		errstr += record_type;
		errstr += " record";
		fatal(errstr);
	}
	return ret;
}

bool read_record(ifstream &ifile, string &record) {
	int temp_int;
	string temp_str;
	char t;

	do {
		t = ifile.get();
		if ( t == EOF ) {
			return false;
		}
	} while ( t == '\n' || t == ' ' );
	record = "";
	record += t;

	switch (t) {
		case 'H': // Header
			record += read_columns(ifile,2,7,'H');
			record += read_hex_columns(ifile,8,19,'H');
			break;
		case 'T': // Text
			record += read_hex_columns(ifile,2,7,'T');
			temp_str = read_hex_columns(ifile,8,9,'T');
			temp_int = hex2int(temp_str);
			record += temp_str;
			record +=  read_hex_columns(ifile,10,9+2*temp_int,'T');
			break;
		case 'E': // End
			record += read_hex_columns(ifile,2,7,'E');
			break;
		default:
			{
				string errstr = "Unknown record type ";
				errstr += t;
				fatal(errstr);
			}
	}
	return true;
}

void record_to_memory(const string record, program &p) {
	const char * c_record = record.c_str();
	switch (*c_record) {
		case 'H': // Header
			if ( p.name.length() != 0 ) {
				fatal("Multiple H records");
			}
			p.name = record.substr(1,6);
			p.starting_address = record.substr(7,6);
			p.length_of_program = hex2int(record.substr(13,6));
			break;
		case 'T': // Text
			{
				int text_start = hex2int(record.substr(1,6));
				int bit_length = hex2int(record.substr(7,2));
				for ( int i = 0 ; i < bit_length ; i++ ) {
					p.memory[i+text_start] = hex2int(record.substr(9+2*i,2));
					p.byte_type_guess[i+text_start] = UNKNOWN;
				}
			}
			break;
		case 'E': // End
			if ( p.first_executable_instruction.length() != 0 ) {
				fatal("Multiple E records");
			}
			p.first_executable_instruction = record.substr(1,6);
			break;
		default:
			{
				string errstr;
				errstr += "Unknown record type ";
				errstr += *c_record;
				fatal(errstr);
			}
	}
}

void mark_code_data(program &p, int location, ByteTypeGuess btg) {
	if ( location >= hex2int(p.starting_address) + p.length_of_program ) {
		return;
	}
	switch (btg) {
		case UNINITIALIZED:
		case UNKNOWN:
			break;
		case CHAR_DATA:
		case UNINITIALIZED_CHAR_DATA:
			{
				if ( p.byte_type_guess[location] != UNKNOWN &&
					 p.byte_type_guess[location] != UNINITIALIZED ) {
					break;
				}
				if ( p.byte_type_guess[location] == UNINITIALIZED )
					btg = UNINITIALIZED_CHAR_DATA;
				p.byte_type_guess[location] = btg;
				break;
			}
		case WORD_DATA:
		case UNINITIALIZED_WORD_DATA:
			{
				if ( p.byte_type_guess[location] != UNKNOWN &&
					 p.byte_type_guess[location] != UNINITIALIZED ) {
					break;
				}
				if ( p.byte_type_guess[location] == UNINITIALIZED )
					btg = UNINITIALIZED_WORD_DATA;
				p.byte_type_guess[location+0] = btg;
				p.byte_type_guess[location+1] = btg;
				p.byte_type_guess[location+2] = btg;
				break;
			}
		case CODE:
			{
				if ( p.byte_type_guess[location] == UNINITIALIZED ) {
					fatal("Attempting to use uninitialized section as code");
				}
				if ( p.byte_type_guess[location] == CODE ) {
					break;
				}
				p.byte_type_guess[location+0] = btg;
				p.byte_type_guess[location+1] = btg;
				p.byte_type_guess[location+2] = btg;

				string opcode_val = byte2hex(p.memory[location]);
				string opcode;
				string operand = byte2hex(p.memory[location+1])+byte2hex(p.memory[location+2]);
				operand = int2hex(hex2int(operand)&0x7FFF); // remove index flag
				if ( ! find_from_symtab(opcode,opcode_val) ) {
					string errstr;
					errstr += "Unknown opcode ";
					errstr += opcode_val;
					errstr += " at location ";
					errstr += int2hex(location);
					fatal(errstr);
				}

				if ( opcode == "ADD"  ||
					 opcode == "AND"  ||
					 opcode == "COMP" ||
					 opcode == "DIV"  ||
					 opcode == "LDA"  ||
					 opcode == "LDL"  ||
					 opcode == "LDX"  ||
					 opcode == "MUL"  ||
					 opcode == "OR"   ||
					 opcode == "STA"  ||
					 opcode == "STL"  ||
					 opcode == "STX"  ||
					 opcode == "SUB"  ||
					 opcode == "TIX" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),WORD_DATA);
				}

				if ( opcode == "LDCH" ||
					 opcode == "STCH" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),CHAR_DATA);
				}

				if ( opcode == "J"    ||
					 opcode == "JEQ"  ||
					 opcode == "JGT"  ||
					 opcode == "JLT"  ||
					 opcode == "JSUB" ||
					 opcode == "RSUB" ) {
					give_label(operand);
					p.is_labelled[hex2int(operand)] = true;
					mark_code_data(p,hex2int(operand),CODE);
				}

				if ( opcode != "J" &&
					 opcode != "RSUB" ) {
					mark_code_data(p,location+3,btg);
				}

				break;
			}
	}
}

void analyze_code_data(program &p) {
	if ( p.name.length() == 0 ) {
		fatal("No Header record");
	}
	if ( p.first_executable_instruction.length() == 0 ) {
		fatal("No End record");
	}
	initialize_symtab();
	add_to_symtab("FIRST",p.first_executable_instruction);
	p.is_labelled[hex2int(p.first_executable_instruction)] = true;
	mark_code_data(p,hex2int(p.first_executable_instruction),CODE);
}

string asm_to_line(string label, string opcode, string operand, bool is_indexed) {
	const int labellength = 8;
	const int opcodelength = 6;
	string ret = "";
	ret += label + string(labellength-label.length(),' ');
	ret += opcode + string(opcodelength-opcode.length(),' ');
	ret += operand;
	if ( is_indexed ) {
		ret += ",X";
	}
	ret += '\n';
	return ret;
}

void write_assembly(const program &p, ofstream &ofile) {
	ofile << asm_to_line(p.name,"START",p.starting_address,false);
	int start_of_program = hex2int(p.starting_address);
	int end_of_program = start_of_program + p.length_of_program;

	for ( int locctr = start_of_program ; locctr < end_of_program ; ) {
		string label = "";
		string opcode = "";
		string operand = "";
		bool is_indexed = false;

		if ( p.is_labelled[locctr] ) {
			if ( !find_from_symtab(label,int2hex(locctr)) ) {
				string errstr;
				errstr += "Label not created for location ";
				errstr += int2hex(locctr);
				error(errstr);
			}
		}

		if ( p.byte_type_guess[locctr] == CODE ) {
			if ( ! find_from_symtab(opcode,byte2hex(p.memory[locctr])) ) {
				string errstr;
				errstr += "Unknown opcode ";
				errstr += opcode;
				errstr += " at location ";
				errstr += int2hex(locctr);
				fatal(errstr);
			}
			operand = byte2hex(p.memory[locctr+1])+byte2hex(p.memory[locctr+2]);
			is_indexed = (hex2int(operand)&0x8000);
			operand = int2hex(hex2int(operand)&0x7FFF); // remove index flag

			if ( opcode != "RD" &&
				 opcode != "TD" &&
				 opcode != "WD" &&
				 opcode != "RSUB" ) {
				if ( !find_from_symtab(operand,operand) ) {
					string errstr;
					errstr += "Label not created for operand ";
					errstr += operand;
					error(errstr);
				}
			}

			if ( opcode == "RSUB" ) {
				operand = "";
			}

			locctr += 3;
		} else if ( p.byte_type_guess[locctr] == CHAR_DATA ) {
			vector<char> byte_list;
			bool type_c = true;
			do {
				byte_list.push_back(p.memory[locctr]);
				if ( !isprint(p.memory[locctr]) ) {
					type_c = false;
				}
				locctr++;
			} while ( p.byte_type_guess[locctr] == CHAR_DATA && !p.is_labelled[locctr] );

			opcode = "CHAR";

			operand += (type_c?"C":"X");
			operand += "'";
			for ( int i = 0 ; i < (int)byte_list.size() ; i++ ) {
				if ( type_c ) {
					operand += byte_list[i];
				} else {
					operand += byte2hex(byte_list[i]);
				}
			}
			operand += "'";
		} else {
			// TODO
			locctr++; // temporarily
		}
		ofile << asm_to_line(label,opcode,operand,is_indexed);
	}
	ofile << asm_to_line("","END","FIRST",false);
}<|endoftext|>