<commit_before>//===-- PPCInstPrinter.cpp - Convert PPC MCInst to assembly syntax --------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints an PPC MCInst to a .s file.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "asm-printer"
#include "PPCInstPrinter.h"
#include "PPCPredicates.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
//#include "llvm/MC/MCAsmInfo.h"
//#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#define GET_INSTRUCTION_NAME
#define PPCAsmPrinter PPCInstPrinter
#define MachineInstr MCInst
#include "PPCGenAsmWriter.inc"

StringRef PPCInstPrinter::getOpcodeName(unsigned Opcode) const {
  return getInstructionName(Opcode);
}

void PPCInstPrinter::printInst(const MCInst *MI, raw_ostream &O) {
  // TODO: pseudo ops.

  // Check for slwi/srwi mnemonics.
  if (MI->getOpcode() == PPC::RLWINM) {
    unsigned char SH = MI->getOperand(2).getImm();
    unsigned char MB = MI->getOperand(3).getImm();
    unsigned char ME = MI->getOperand(4).getImm();
    bool useSubstituteMnemonic = false;
    if (SH <= 31 && MB == 0 && ME == (31-SH)) {
      O << "\tslwi "; useSubstituteMnemonic = true;
    }
    if (SH <= 31 && MB == (32-SH) && ME == 31) {
      O << "\tsrwi "; useSubstituteMnemonic = true;
      SH = 32-SH;
    }
    if (useSubstituteMnemonic) {
      printOperand(MI, 0, O);
      O << ", ";
      printOperand(MI, 1, O);
      O << ", " << (unsigned int)SH;
      return;
    }
  }

  if ((MI->getOpcode() == PPC::OR || MI->getOpcode() == PPC::OR8) &&
      MI->getOperand(1).getReg() == MI->getOperand(2).getReg()) {
    O << "\tmr ";
    printOperand(MI, 0, O);
    O << ", ";
    printOperand(MI, 1, O);
    return;
  }

  if (MI->getOpcode() == PPC::RLDICR) {
    unsigned char SH = MI->getOperand(2).getImm();
    unsigned char ME = MI->getOperand(3).getImm();
    // rldicr RA, RS, SH, 63-SH == sldi RA, RS, SH
    if (63-SH == ME) {
      O << "\tsldi ";
      printOperand(MI, 0, O);
      O << ", ";
      printOperand(MI, 1, O);
      O << ", " << (unsigned int)SH;
      return;
    }
  }

  printInstruction(MI, O);
}

void PPCInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNo,
                                           raw_ostream &O,
                                           const char *Modifier) {
  assert(Modifier && "Must specify 'cc' or 'reg' as predicate op modifier!");
  unsigned Code = MI->getOperand(OpNo).getImm();
  if (StringRef(Modifier) == "cc") {
    switch ((PPC::Predicate)Code) {
    default: assert(0 && "Invalid predicate");
    case PPC::PRED_ALWAYS: return; // Don't print anything for always.
    case PPC::PRED_LT: O << "lt"; return;
    case PPC::PRED_LE: O << "le"; return;
    case PPC::PRED_EQ: O << "eq"; return;
    case PPC::PRED_GE: O << "ge"; return;
    case PPC::PRED_GT: O << "gt"; return;
    case PPC::PRED_NE: O << "ne"; return;
    case PPC::PRED_UN: O << "un"; return;
    case PPC::PRED_NU: O << "nu"; return;
    }
  }

  assert(StringRef(Modifier) == "reg" &&
         "Need to specify 'cc' or 'reg' as predicate op modifier!");
  // Don't print the register for 'always'.
  if (Code == PPC::PRED_ALWAYS) return;
  printOperand(MI, OpNo+1, O);
}

void PPCInstPrinter::printS5ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  char Value = MI->getOperand(OpNo).getImm();
  Value = (Value << (32-5)) >> (32-5);
  O << (int)Value;
}

void PPCInstPrinter::printU5ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  unsigned char Value = MI->getOperand(OpNo).getImm();
  assert(Value <= 31 && "Invalid u5imm argument!");
  O << (unsigned int)Value;
}

void PPCInstPrinter::printU6ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  unsigned char Value = MI->getOperand(OpNo).getImm();
  assert(Value <= 63 && "Invalid u6imm argument!");
  O << (unsigned int)Value;
}

void PPCInstPrinter::printS16ImmOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  O << (short)MI->getOperand(OpNo).getImm();
}

void PPCInstPrinter::printU16ImmOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  O << (unsigned short)MI->getOperand(OpNo).getImm();
}

void PPCInstPrinter::printS16X4ImmOperand(const MCInst *MI, unsigned OpNo,
                                          raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    O << (short)(MI->getOperand(OpNo).getImm()*4);
  else
    printOperand(MI, OpNo, O);
}

void PPCInstPrinter::printBranchOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  if (!MI->getOperand(OpNo).isImm())
    return printOperand(MI, OpNo, O);

  // Branches can take an immediate operand.  This is used by the branch
  // selection pass to print $+8, an eight byte displacement from the PC.
  O << "$+";
  printAbsAddrOperand(MI, OpNo, O);
}

void PPCInstPrinter::printAbsAddrOperand(const MCInst *MI, unsigned OpNo,
                                         raw_ostream &O) {
  O << (int)MI->getOperand(OpNo).getImm()*4;
}

void PPCInstPrinter::printcrbitm(const MCInst *MI, unsigned OpNo,
                                 raw_ostream &O) {
  unsigned CCReg = MI->getOperand(OpNo).getReg();
  unsigned RegNo;
  switch (CCReg) {
  default: assert(0 && "Unknown CR register");
  case PPC::CR0: RegNo = 0; break;
  case PPC::CR1: RegNo = 1; break;
  case PPC::CR2: RegNo = 2; break;
  case PPC::CR3: RegNo = 3; break;
  case PPC::CR4: RegNo = 4; break;
  case PPC::CR5: RegNo = 5; break;
  case PPC::CR6: RegNo = 6; break;
  case PPC::CR7: RegNo = 7; break;
  }
  O << (0x80 >> RegNo);
}

void PPCInstPrinter::printMemRegImm(const MCInst *MI, unsigned OpNo,
                                    raw_ostream &O) {
  printSymbolLo(MI, OpNo, O);
  O << '(';
  assert(MI->getOperand(OpNo+1).isReg() && "Bad operand");
  // FIXME: Simplify.
  if (MI->getOperand(OpNo+1).isReg() &&
      MI->getOperand(OpNo+1).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo+1, O);
  O << ')';
}

void PPCInstPrinter::printMemRegImmShifted(const MCInst *MI, unsigned OpNo,
                                           raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    printS16X4ImmOperand(MI, OpNo, O);
  else
    printSymbolLo(MI, OpNo, O);
  O << '(';

  assert(MI->getOperand(OpNo+1).isReg() && "Bad operand");
  // FIXME: Simplify.
  if (MI->getOperand(OpNo+1).isReg() &&
      MI->getOperand(OpNo+1).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo+1, O);
  O << ')';
}

void PPCInstPrinter::printMemRegReg(const MCInst *MI, unsigned OpNo,
                                    raw_ostream &O) {
  // When used as the base register, r0 reads constant zero rather than
  // the value contained in the register.  For this reason, the darwin
  // assembler requires that we print r0 as 0 (no r) when used as the base.
  if (MI->getOperand(OpNo).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo, O);
  O << ", ";
  printOperand(MI, OpNo+1, O);
}

/// stripRegisterPrefix - This method strips the character prefix from a
/// register name so that only the number is left.  Used by for linux asm.
const char *stripRegisterPrefix(const char *RegName) {
  switch (RegName[0]) {
  case 'r':
  case 'f':
  case 'v': return RegName + 1;
  case 'c': if (RegName[1] == 'r') return RegName + 2;
  }

  return RegName;
}

void PPCInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                  raw_ostream &O) {
  const MCOperand &Op = MI->getOperand(OpNo);
  if (Op.isReg()) {
    const char *RegName = getRegisterName(Op.getReg());
    // The linux and AIX assembler does not take register prefixes.
    if (!isDarwinSyntax())
      RegName = stripRegisterPrefix(RegName);

    O << RegName;
    return;
  }

  if (Op.isImm()) {
    O << Op.getImm();
    return;
  }

  assert(Op.isExpr() && "unknown operand kind in printOperand");
  O << *Op.getExpr();
}

void PPCInstPrinter::printSymbolLo(const MCInst *MI, unsigned OpNo,
                                   raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    return printS16ImmOperand(MI, OpNo, O);

  // FIXME: This is a terrible hack because we can't encode lo16() as an operand
  // flag of a subtraction.  See the FIXME in GetSymbolRef in PPCMCInstLower.
  if (MI->getOperand(OpNo).isExpr() &&
      isa<MCBinaryExpr>(MI->getOperand(OpNo).getExpr())) {
    O << "lo16(";
    printOperand(MI, OpNo, O);
    O << ')';
  } else {
    printOperand(MI, OpNo, O);
  }
}

void PPCInstPrinter::printSymbolHi(const MCInst *MI, unsigned OpNo,
                                   raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    return printS16ImmOperand(MI, OpNo, O);

  // FIXME: This is a terrible hack because we can't encode lo16() as an operand
  // flag of a subtraction.  See the FIXME in GetSymbolRef in PPCMCInstLower.
  if (MI->getOperand(OpNo).isExpr() &&
      isa<MCBinaryExpr>(MI->getOperand(OpNo).getExpr())) {
    O << "ha16(";
    printOperand(MI, OpNo, O);
    O << ')';
  } else {
    printOperand(MI, OpNo, O);
  }
}

<commit_msg>fix some fixme's, removing dead code.<commit_after>//===-- PPCInstPrinter.cpp - Convert PPC MCInst to assembly syntax --------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints an PPC MCInst to a .s file.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "asm-printer"
#include "PPCInstPrinter.h"
#include "PPCPredicates.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
//#include "llvm/MC/MCAsmInfo.h"
//#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#define GET_INSTRUCTION_NAME
#define PPCAsmPrinter PPCInstPrinter
#define MachineInstr MCInst
#include "PPCGenAsmWriter.inc"

StringRef PPCInstPrinter::getOpcodeName(unsigned Opcode) const {
  return getInstructionName(Opcode);
}

void PPCInstPrinter::printInst(const MCInst *MI, raw_ostream &O) {
  // TODO: pseudo ops.

  // Check for slwi/srwi mnemonics.
  if (MI->getOpcode() == PPC::RLWINM) {
    unsigned char SH = MI->getOperand(2).getImm();
    unsigned char MB = MI->getOperand(3).getImm();
    unsigned char ME = MI->getOperand(4).getImm();
    bool useSubstituteMnemonic = false;
    if (SH <= 31 && MB == 0 && ME == (31-SH)) {
      O << "\tslwi "; useSubstituteMnemonic = true;
    }
    if (SH <= 31 && MB == (32-SH) && ME == 31) {
      O << "\tsrwi "; useSubstituteMnemonic = true;
      SH = 32-SH;
    }
    if (useSubstituteMnemonic) {
      printOperand(MI, 0, O);
      O << ", ";
      printOperand(MI, 1, O);
      O << ", " << (unsigned int)SH;
      return;
    }
  }

  if ((MI->getOpcode() == PPC::OR || MI->getOpcode() == PPC::OR8) &&
      MI->getOperand(1).getReg() == MI->getOperand(2).getReg()) {
    O << "\tmr ";
    printOperand(MI, 0, O);
    O << ", ";
    printOperand(MI, 1, O);
    return;
  }

  if (MI->getOpcode() == PPC::RLDICR) {
    unsigned char SH = MI->getOperand(2).getImm();
    unsigned char ME = MI->getOperand(3).getImm();
    // rldicr RA, RS, SH, 63-SH == sldi RA, RS, SH
    if (63-SH == ME) {
      O << "\tsldi ";
      printOperand(MI, 0, O);
      O << ", ";
      printOperand(MI, 1, O);
      O << ", " << (unsigned int)SH;
      return;
    }
  }

  printInstruction(MI, O);
}

void PPCInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNo,
                                           raw_ostream &O,
                                           const char *Modifier) {
  assert(Modifier && "Must specify 'cc' or 'reg' as predicate op modifier!");
  unsigned Code = MI->getOperand(OpNo).getImm();
  if (StringRef(Modifier) == "cc") {
    switch ((PPC::Predicate)Code) {
    default: assert(0 && "Invalid predicate");
    case PPC::PRED_ALWAYS: return; // Don't print anything for always.
    case PPC::PRED_LT: O << "lt"; return;
    case PPC::PRED_LE: O << "le"; return;
    case PPC::PRED_EQ: O << "eq"; return;
    case PPC::PRED_GE: O << "ge"; return;
    case PPC::PRED_GT: O << "gt"; return;
    case PPC::PRED_NE: O << "ne"; return;
    case PPC::PRED_UN: O << "un"; return;
    case PPC::PRED_NU: O << "nu"; return;
    }
  }

  assert(StringRef(Modifier) == "reg" &&
         "Need to specify 'cc' or 'reg' as predicate op modifier!");
  // Don't print the register for 'always'.
  if (Code == PPC::PRED_ALWAYS) return;
  printOperand(MI, OpNo+1, O);
}

void PPCInstPrinter::printS5ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  char Value = MI->getOperand(OpNo).getImm();
  Value = (Value << (32-5)) >> (32-5);
  O << (int)Value;
}

void PPCInstPrinter::printU5ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  unsigned char Value = MI->getOperand(OpNo).getImm();
  assert(Value <= 31 && "Invalid u5imm argument!");
  O << (unsigned int)Value;
}

void PPCInstPrinter::printU6ImmOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  unsigned char Value = MI->getOperand(OpNo).getImm();
  assert(Value <= 63 && "Invalid u6imm argument!");
  O << (unsigned int)Value;
}

void PPCInstPrinter::printS16ImmOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  O << (short)MI->getOperand(OpNo).getImm();
}

void PPCInstPrinter::printU16ImmOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  O << (unsigned short)MI->getOperand(OpNo).getImm();
}

void PPCInstPrinter::printS16X4ImmOperand(const MCInst *MI, unsigned OpNo,
                                          raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    O << (short)(MI->getOperand(OpNo).getImm()*4);
  else
    printOperand(MI, OpNo, O);
}

void PPCInstPrinter::printBranchOperand(const MCInst *MI, unsigned OpNo,
                                        raw_ostream &O) {
  if (!MI->getOperand(OpNo).isImm())
    return printOperand(MI, OpNo, O);

  // Branches can take an immediate operand.  This is used by the branch
  // selection pass to print $+8, an eight byte displacement from the PC.
  O << "$+";
  printAbsAddrOperand(MI, OpNo, O);
}

void PPCInstPrinter::printAbsAddrOperand(const MCInst *MI, unsigned OpNo,
                                         raw_ostream &O) {
  O << (int)MI->getOperand(OpNo).getImm()*4;
}

void PPCInstPrinter::printcrbitm(const MCInst *MI, unsigned OpNo,
                                 raw_ostream &O) {
  unsigned CCReg = MI->getOperand(OpNo).getReg();
  unsigned RegNo;
  switch (CCReg) {
  default: assert(0 && "Unknown CR register");
  case PPC::CR0: RegNo = 0; break;
  case PPC::CR1: RegNo = 1; break;
  case PPC::CR2: RegNo = 2; break;
  case PPC::CR3: RegNo = 3; break;
  case PPC::CR4: RegNo = 4; break;
  case PPC::CR5: RegNo = 5; break;
  case PPC::CR6: RegNo = 6; break;
  case PPC::CR7: RegNo = 7; break;
  }
  O << (0x80 >> RegNo);
}

void PPCInstPrinter::printMemRegImm(const MCInst *MI, unsigned OpNo,
                                    raw_ostream &O) {
  printSymbolLo(MI, OpNo, O);
  O << '(';
  if (MI->getOperand(OpNo+1).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo+1, O);
  O << ')';
}

void PPCInstPrinter::printMemRegImmShifted(const MCInst *MI, unsigned OpNo,
                                           raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    printS16X4ImmOperand(MI, OpNo, O);
  else
    printSymbolLo(MI, OpNo, O);
  O << '(';

  if (MI->getOperand(OpNo+1).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo+1, O);
  O << ')';
}

void PPCInstPrinter::printMemRegReg(const MCInst *MI, unsigned OpNo,
                                    raw_ostream &O) {
  // When used as the base register, r0 reads constant zero rather than
  // the value contained in the register.  For this reason, the darwin
  // assembler requires that we print r0 as 0 (no r) when used as the base.
  if (MI->getOperand(OpNo).getReg() == PPC::R0)
    O << "0";
  else
    printOperand(MI, OpNo, O);
  O << ", ";
  printOperand(MI, OpNo+1, O);
}

/// stripRegisterPrefix - This method strips the character prefix from a
/// register name so that only the number is left.  Used by for linux asm.
const char *stripRegisterPrefix(const char *RegName) {
  switch (RegName[0]) {
  case 'r':
  case 'f':
  case 'v': return RegName + 1;
  case 'c': if (RegName[1] == 'r') return RegName + 2;
  }

  return RegName;
}

void PPCInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                  raw_ostream &O) {
  const MCOperand &Op = MI->getOperand(OpNo);
  if (Op.isReg()) {
    const char *RegName = getRegisterName(Op.getReg());
    // The linux and AIX assembler does not take register prefixes.
    if (!isDarwinSyntax())
      RegName = stripRegisterPrefix(RegName);

    O << RegName;
    return;
  }

  if (Op.isImm()) {
    O << Op.getImm();
    return;
  }

  assert(Op.isExpr() && "unknown operand kind in printOperand");
  O << *Op.getExpr();
}

void PPCInstPrinter::printSymbolLo(const MCInst *MI, unsigned OpNo,
                                   raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    return printS16ImmOperand(MI, OpNo, O);

  // FIXME: This is a terrible hack because we can't encode lo16() as an operand
  // flag of a subtraction.  See the FIXME in GetSymbolRef in PPCMCInstLower.
  if (MI->getOperand(OpNo).isExpr() &&
      isa<MCBinaryExpr>(MI->getOperand(OpNo).getExpr())) {
    O << "lo16(";
    printOperand(MI, OpNo, O);
    O << ')';
  } else {
    printOperand(MI, OpNo, O);
  }
}

void PPCInstPrinter::printSymbolHi(const MCInst *MI, unsigned OpNo,
                                   raw_ostream &O) {
  if (MI->getOperand(OpNo).isImm())
    return printS16ImmOperand(MI, OpNo, O);

  // FIXME: This is a terrible hack because we can't encode lo16() as an operand
  // flag of a subtraction.  See the FIXME in GetSymbolRef in PPCMCInstLower.
  if (MI->getOperand(OpNo).isExpr() &&
      isa<MCBinaryExpr>(MI->getOperand(OpNo).getExpr())) {
    O << "ha16(";
    printOperand(MI, OpNo, O);
    O << ')';
  } else {
    printOperand(MI, OpNo, O);
  }
}

<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include "seec/Clang/Compile.hpp"
#include "seec/Clang/MappedAST.hpp"

#include "clang/AST/RecursiveASTVisitor.h"

#include "llvm/Constants.h"
#include "llvm/Instruction.h"

using namespace clang;
using namespace llvm;

namespace seec {

namespace seec_clang {

//===----------------------------------------------------------------------===//
// class Mapper
//===----------------------------------------------------------------------===//

class MappingASTVisitor : public RecursiveASTVisitor<MappingASTVisitor> {
  std::vector<Decl const *> &Decls;
  std::vector<Stmt const *> &Stmts;

public:
  MappingASTVisitor(std::vector<Decl const *> &Decls,
                    std::vector<Stmt const *> &Stmts)
  : Decls(Decls),
    Stmts(Stmts)
  {}

  /// RecursiveASTVisitor Methods
  /// \{

  bool VisitDecl(Decl *D) {
    Decls.push_back(D);
    return true;
  }

  bool VisitStmt(Stmt *S) {
    Stmts.push_back(S);
    return true;
  }

  /// \}
};

//===----------------------------------------------------------------------===//
// class MappedAST
//===----------------------------------------------------------------------===//

MappedAST::~MappedAST() {
  llvm::errs() << "~MappedAST @" << this
               << " with AST@" << AST << "\n";

  delete AST;

  llvm::errs() << "...done\n";
}

std::unique_ptr<MappedAST>
MappedAST::FromASTUnit(clang::ASTUnit *AST) {
  if (!AST)
    return nullptr;

  std::vector<Decl const *> Decls;
  std::vector<Stmt const *> Stmts;

  MappingASTVisitor Mapper(Decls, Stmts);

  for (auto It = AST->top_level_begin(), End = AST->top_level_end();
       It != End; ++It) {
    Mapper.TraverseDecl(*It);
  }

  auto Mapped = std::unique_ptr<MappedAST>(new MappedAST(AST,
                                                         std::move(Decls),
                                                         std::move(Stmts)));
  if (!Mapped)
    delete AST;

  return Mapped;
}

std::unique_ptr<MappedAST>
MappedAST::LoadFromASTFile(llvm::StringRef Filename,
                           IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
                           FileSystemOptions const &FileSystemOpts) {
  return MappedAST::FromASTUnit(
          ASTUnit::LoadFromASTFile(Filename.str(), Diags, FileSystemOpts));
}

std::unique_ptr<MappedAST>
MappedAST::LoadFromCompilerInvocation(
  std::unique_ptr<CompilerInvocation> Invocation,
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags)
{
  return MappedAST::FromASTUnit(
          ASTUnit::LoadFromCompilerInvocation(Invocation.get(), Diags));
}

//===----------------------------------------------------------------------===//
// class MappedModule
//===----------------------------------------------------------------------===//

llvm::sys::Path getPathFromFileNode(llvm::MDNode const *FileNode) {
  auto FilenameStr = dyn_cast<MDString>(FileNode->getOperand(0u));
  if (!FilenameStr)
    return llvm::sys::Path();

  auto PathStr = dyn_cast<MDString>(FileNode->getOperand(1u));
  if (!PathStr)
    return llvm::sys::Path();

  auto FilePath = llvm::sys::Path(PathStr->getString());
  FilePath.appendComponent(FilenameStr->getString());

  return std::move(FilePath);
}

MappedAST const *MappedModule::getASTForFile(llvm::MDNode const *FileNode) {
  // check lookup to see if we've already loaded the AST
  auto It = ASTLookup.find(FileNode);
  if (It != ASTLookup.end())
    return It->second;

  // if not, try to load the AST from the source file
  auto FilePath = getPathFromFileNode(FileNode);
  if (FilePath.empty())
    return nullptr;

  auto CI = GetCompileForSourceFile(FilePath.c_str(),
                                    ExecutablePath,
                                    Diags);
  if (!CI) {
    ASTLookup[FileNode] = nullptr;
    return nullptr;
  }

  auto AST = MappedAST::LoadFromCompilerInvocation(std::move(CI), Diags);
  if (!AST) {
    ASTLookup[FileNode] = nullptr;
    return nullptr;
  }

  // get the raw pointer, because we have to push the unique_ptr onto the list
  auto ASTRaw = AST.get();

  ASTLookup[FileNode] = ASTRaw;
  ASTList.push_back(std::move(AST));

  return ASTRaw;
}

std::vector<MappedGlobalDecl> MappedModule::getMappedGlobalDecls() {
  std::vector<MappedGlobalDecl> List;

  auto GlobalIdxMD = Module.getNamedMetadata(MDGlobalDeclIdxsStr);
  if (!GlobalIdxMD)
    return std::move(List);

  for (auto i = 0u; i < GlobalIdxMD->getNumOperands(); ++i) {
    auto Node = GlobalIdxMD->getOperand(i);
    assert(Node->getNumOperands() == 3);

    auto FileNode = dyn_cast<MDNode>(Node->getOperand(0u));
    assert(FileNode);

    auto AST = getASTForFile(FileNode);
    assert(AST);

    auto FilePath = getPathFromFileNode(FileNode);
    assert(!FilePath.empty());

    auto Func = dyn_cast<Function>(Node->getOperand(1u));
    assert(Func);

    auto DeclIdx = dyn_cast<ConstantInt>(Node->getOperand(2u));
    assert(DeclIdx);

    auto Decl = AST->getDeclFromIdx(DeclIdx->getZExtValue());

    List.emplace_back(std::move(FilePath), Decl, Func);
  }

  return std::move(List);
}

Decl const *MappedModule::getDecl(Instruction const *I) {
  auto DeclIdxNode = I->getMetadata(MDDeclIdxKind);
  if (!DeclIdxNode)
    return nullptr;

  auto FileNode = dyn_cast<MDNode>(DeclIdxNode->getOperand(0));
  if (!FileNode)
    return nullptr;

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return nullptr;

  ConstantInt const *CI = dyn_cast<ConstantInt>(DeclIdxNode->getOperand(1));
  if (!CI)
    return nullptr;

  return AST->getDeclFromIdx(CI->getZExtValue());
}

std::pair<clang::Decl const *, MappedAST const *>
MappedModule::getDeclAndMappedAST(llvm::Instruction const *I) {
  auto DeclIdxNode = I->getMetadata(MDDeclIdxKind);
  if (!DeclIdxNode)
    return std::make_pair(nullptr, nullptr);

  auto FileNode = dyn_cast<MDNode>(DeclIdxNode->getOperand(0));
  if (!FileNode)
    return std::make_pair(nullptr, nullptr);

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return std::make_pair(nullptr, nullptr);

  ConstantInt const *CI = dyn_cast<ConstantInt>(DeclIdxNode->getOperand(1));
  if (!CI)
    return std::make_pair(nullptr, nullptr);

  return std::make_pair(AST->getDeclFromIdx(CI->getZExtValue()), AST);
}

Stmt const *MappedModule::getStmt(Instruction const *I) {
  auto StmtIdxNode = I->getMetadata(MDStmtIdxKind);
  if (!StmtIdxNode)
    return nullptr;

  auto FileNode = dyn_cast<MDNode>(StmtIdxNode->getOperand(0));
  if (!FileNode)
    return nullptr;

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return nullptr;

  ConstantInt const *CI = dyn_cast<ConstantInt>(StmtIdxNode->getOperand(1));
  if (!CI)
    return nullptr;

  return AST->getStmtFromIdx(CI->getZExtValue());
}

std::pair<clang::Stmt const *, MappedAST const *>
MappedModule::getStmtAndMappedAST(llvm::Instruction const *I) {
  auto StmtIdxNode = I->getMetadata(MDStmtIdxKind);
  if (!StmtIdxNode)
    return std::make_pair(nullptr, nullptr);

  auto FileNode = dyn_cast<MDNode>(StmtIdxNode->getOperand(0));
  if (!FileNode)
    return std::make_pair(nullptr, nullptr);

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return std::make_pair(nullptr, nullptr);

  ConstantInt const *CI = dyn_cast<ConstantInt>(StmtIdxNode->getOperand(1));
  if (!CI)
    return std::make_pair(nullptr, nullptr);

  return std::make_pair(AST->getStmtFromIdx(CI->getZExtValue()), AST);
}

} // namespace seec_clang (in seec)

} // namespace seec
<commit_msg>Correctly release ownship of CompilerInvocation when passing to ASTUnit::LoadFromCompilerInvocation.<commit_after>//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include "seec/Clang/Compile.hpp"
#include "seec/Clang/MappedAST.hpp"

#include "clang/AST/RecursiveASTVisitor.h"

#include "llvm/Constants.h"
#include "llvm/Instruction.h"

using namespace clang;
using namespace llvm;

namespace seec {

namespace seec_clang {

//===----------------------------------------------------------------------===//
// class Mapper
//===----------------------------------------------------------------------===//

class MappingASTVisitor : public RecursiveASTVisitor<MappingASTVisitor> {
  std::vector<Decl const *> &Decls;
  std::vector<Stmt const *> &Stmts;

public:
  MappingASTVisitor(std::vector<Decl const *> &Decls,
                    std::vector<Stmt const *> &Stmts)
  : Decls(Decls),
    Stmts(Stmts)
  {}

  /// RecursiveASTVisitor Methods
  /// \{

  bool VisitDecl(Decl *D) {
    Decls.push_back(D);
    return true;
  }

  bool VisitStmt(Stmt *S) {
    Stmts.push_back(S);
    return true;
  }

  /// \}
};

//===----------------------------------------------------------------------===//
// class MappedAST
//===----------------------------------------------------------------------===//

MappedAST::~MappedAST() {
  llvm::errs() << "~MappedAST @" << this
               << " with AST@" << AST << "\n";

  delete AST;

  llvm::errs() << "...done\n";
}

std::unique_ptr<MappedAST>
MappedAST::FromASTUnit(clang::ASTUnit *AST) {
  if (!AST)
    return nullptr;

  std::vector<Decl const *> Decls;
  std::vector<Stmt const *> Stmts;

  MappingASTVisitor Mapper(Decls, Stmts);

  for (auto It = AST->top_level_begin(), End = AST->top_level_end();
       It != End; ++It) {
    Mapper.TraverseDecl(*It);
  }

  auto Mapped = std::unique_ptr<MappedAST>(new MappedAST(AST,
                                                         std::move(Decls),
                                                         std::move(Stmts)));
  if (!Mapped)
    delete AST;

  return Mapped;
}

std::unique_ptr<MappedAST>
MappedAST::LoadFromASTFile(llvm::StringRef Filename,
                           IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
                           FileSystemOptions const &FileSystemOpts) {
  return MappedAST::FromASTUnit(
          ASTUnit::LoadFromASTFile(Filename.str(), Diags, FileSystemOpts));
}

std::unique_ptr<MappedAST>
MappedAST::LoadFromCompilerInvocation(
  std::unique_ptr<CompilerInvocation> Invocation,
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags)
{
  return MappedAST::FromASTUnit(
          ASTUnit::LoadFromCompilerInvocation(Invocation.release(), Diags));
}

//===----------------------------------------------------------------------===//
// class MappedModule
//===----------------------------------------------------------------------===//

llvm::sys::Path getPathFromFileNode(llvm::MDNode const *FileNode) {
  auto FilenameStr = dyn_cast<MDString>(FileNode->getOperand(0u));
  if (!FilenameStr)
    return llvm::sys::Path();

  auto PathStr = dyn_cast<MDString>(FileNode->getOperand(1u));
  if (!PathStr)
    return llvm::sys::Path();

  auto FilePath = llvm::sys::Path(PathStr->getString());
  FilePath.appendComponent(FilenameStr->getString());

  return std::move(FilePath);
}

MappedAST const *MappedModule::getASTForFile(llvm::MDNode const *FileNode) {
  // check lookup to see if we've already loaded the AST
  auto It = ASTLookup.find(FileNode);
  if (It != ASTLookup.end())
    return It->second;

  // if not, try to load the AST from the source file
  auto FilePath = getPathFromFileNode(FileNode);
  if (FilePath.empty())
    return nullptr;

  auto CI = GetCompileForSourceFile(FilePath.c_str(),
                                    ExecutablePath,
                                    Diags);
  if (!CI) {
    ASTLookup[FileNode] = nullptr;
    return nullptr;
  }

  auto AST = MappedAST::LoadFromCompilerInvocation(std::move(CI), Diags);
  if (!AST) {
    ASTLookup[FileNode] = nullptr;
    return nullptr;
  }

  // get the raw pointer, because we have to push the unique_ptr onto the list
  auto ASTRaw = AST.get();

  ASTLookup[FileNode] = ASTRaw;
  ASTList.push_back(std::move(AST));

  return ASTRaw;
}

std::vector<MappedGlobalDecl> MappedModule::getMappedGlobalDecls() {
  std::vector<MappedGlobalDecl> List;

  auto GlobalIdxMD = Module.getNamedMetadata(MDGlobalDeclIdxsStr);
  if (!GlobalIdxMD)
    return std::move(List);

  for (auto i = 0u; i < GlobalIdxMD->getNumOperands(); ++i) {
    auto Node = GlobalIdxMD->getOperand(i);
    assert(Node->getNumOperands() == 3);

    auto FileNode = dyn_cast<MDNode>(Node->getOperand(0u));
    assert(FileNode);

    auto AST = getASTForFile(FileNode);
    assert(AST);

    auto FilePath = getPathFromFileNode(FileNode);
    assert(!FilePath.empty());

    auto Func = dyn_cast<Function>(Node->getOperand(1u));
    assert(Func);

    auto DeclIdx = dyn_cast<ConstantInt>(Node->getOperand(2u));
    assert(DeclIdx);

    auto Decl = AST->getDeclFromIdx(DeclIdx->getZExtValue());

    List.emplace_back(std::move(FilePath), Decl, Func);
  }

  return std::move(List);
}

Decl const *MappedModule::getDecl(Instruction const *I) {
  auto DeclIdxNode = I->getMetadata(MDDeclIdxKind);
  if (!DeclIdxNode)
    return nullptr;

  auto FileNode = dyn_cast<MDNode>(DeclIdxNode->getOperand(0));
  if (!FileNode)
    return nullptr;

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return nullptr;

  ConstantInt const *CI = dyn_cast<ConstantInt>(DeclIdxNode->getOperand(1));
  if (!CI)
    return nullptr;

  return AST->getDeclFromIdx(CI->getZExtValue());
}

std::pair<clang::Decl const *, MappedAST const *>
MappedModule::getDeclAndMappedAST(llvm::Instruction const *I) {
  auto DeclIdxNode = I->getMetadata(MDDeclIdxKind);
  if (!DeclIdxNode)
    return std::make_pair(nullptr, nullptr);

  auto FileNode = dyn_cast<MDNode>(DeclIdxNode->getOperand(0));
  if (!FileNode)
    return std::make_pair(nullptr, nullptr);

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return std::make_pair(nullptr, nullptr);

  ConstantInt const *CI = dyn_cast<ConstantInt>(DeclIdxNode->getOperand(1));
  if (!CI)
    return std::make_pair(nullptr, nullptr);

  return std::make_pair(AST->getDeclFromIdx(CI->getZExtValue()), AST);
}

Stmt const *MappedModule::getStmt(Instruction const *I) {
  auto StmtIdxNode = I->getMetadata(MDStmtIdxKind);
  if (!StmtIdxNode)
    return nullptr;

  auto FileNode = dyn_cast<MDNode>(StmtIdxNode->getOperand(0));
  if (!FileNode)
    return nullptr;

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return nullptr;

  ConstantInt const *CI = dyn_cast<ConstantInt>(StmtIdxNode->getOperand(1));
  if (!CI)
    return nullptr;

  return AST->getStmtFromIdx(CI->getZExtValue());
}

std::pair<clang::Stmt const *, MappedAST const *>
MappedModule::getStmtAndMappedAST(llvm::Instruction const *I) {
  auto StmtIdxNode = I->getMetadata(MDStmtIdxKind);
  if (!StmtIdxNode)
    return std::make_pair(nullptr, nullptr);

  auto FileNode = dyn_cast<MDNode>(StmtIdxNode->getOperand(0));
  if (!FileNode)
    return std::make_pair(nullptr, nullptr);

  auto AST = getASTForFile(FileNode);
  if (!AST)
    return std::make_pair(nullptr, nullptr);

  ConstantInt const *CI = dyn_cast<ConstantInt>(StmtIdxNode->getOperand(1));
  if (!CI)
    return std::make_pair(nullptr, nullptr);

  return std::make_pair(AST->getStmtFromIdx(CI->getZExtValue()), AST);
}

} // namespace seec_clang (in seec)

} // namespace seec
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE "parse_array<toml::value>_test"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml/parser.hpp>
#include "test_parse_aux.hpp"

using namespace toml;
using namespace detail;

BOOST_AUTO_TEST_CASE(test_oneline_array)
{
    TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[]", array());
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\",  \"baz\"]", a);
    }
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5,]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\",  \"baz\",]", a);
    }
}

BOOST_AUTO_TEST_CASE(test_oneline_array_value)
{
    TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[]", toml::value(array()));
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,1,4,1,5]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", \"bar\",  \"baz\"]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,1,4,1,5,]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", \"bar\",  \"baz\",]", toml::value(a));
    }
}

BOOST_AUTO_TEST_CASE(test_multiline_array)
{
    TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\n#comment\n]", array());
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,\n1,\n4,\n1,\n5]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
    }

    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("b#r");
        a[2] = toml::value("b#z");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
    }
}

BOOST_AUTO_TEST_CASE(test_multiline_array_value)
{
    TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\n#comment\n]", toml::value(array()));
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,\n1,\n4,\n1,\n5]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n\"bar\",\n\"baz\"]", toml::value(a));
    }

    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("b#r");
        a[2] = toml::value("b#z");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a));
    }
}

BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
{
#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES
    BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed.");
#else
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a));
    }
#endif
}

BOOST_AUTO_TEST_CASE(test_comments_after_comma)
{
    {
        array a;
        a.push_back("foo");
        a.push_back("bar");
        a.push_back("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
            "[ \"foo\" # comment\n"
            ", \"bar\" # comment\n"
            ", \"baz\" # comment\n"
            "]", toml::value(a));
    }
}
<commit_msg>test: add comment/no-comment cases to parse_array<commit_after>#define BOOST_TEST_MODULE "parse_array<toml::value>_test"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml/parser.hpp>
#include "test_parse_aux.hpp"

using namespace toml;
using namespace detail;

BOOST_AUTO_TEST_CASE(test_oneline_array)
{
    TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[]", array());
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\",  \"baz\"]", a);
    }
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5,]", a);
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\",  \"baz\",]", a);
    }
}

BOOST_AUTO_TEST_CASE(test_oneline_array_value)
{
    TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[]", toml::value(array()));
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,1,4,1,5]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", \"bar\",  \"baz\"]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
        a[3] = toml::value(1); a[4] = toml::value(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,1,4,1,5,]", toml::value(a));
    }
    {
        array a(3);
        a[0] = toml::value("foo"); a[1] = toml::value("bar");
        a[2] = toml::value("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", \"bar\",  \"baz\",]", toml::value(a));
    }
}

BOOST_AUTO_TEST_CASE(test_multiline_array)
{
    TOML11_TEST_PARSE_EQUAL(parse_array<basic_value< discard_comments>>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type());
    TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\n#comment\n]", typename basic_value<preserve_comments>::array_type());

    {
        typename basic_value<discard_comments>::array_type a(5);
        a[0] = basic_value<discard_comments>(3);
        a[1] = basic_value<discard_comments>(1);
        a[2] = basic_value<discard_comments>(4);
        a[3] = basic_value<discard_comments>(1);
        a[4] = basic_value<discard_comments>(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
    }
    {
        typename basic_value<preserve_comments>::array_type a(5);
        a[0] = basic_value<preserve_comments>(3);
        a[1] = basic_value<preserve_comments>(1);
        a[2] = basic_value<preserve_comments>(4);
        a[3] = basic_value<preserve_comments>(1);
        a[4] = basic_value<preserve_comments>(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
    }

    {
        typename basic_value<discard_comments>::array_type a(5);
        a[0] = basic_value<discard_comments>(3);
        a[1] = basic_value<discard_comments>(1);
        a[2] = basic_value<discard_comments>(4);
        a[3] = basic_value<discard_comments>(1);
        a[4] = basic_value<discard_comments>(5);
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
    }
    {
        typename basic_value<preserve_comments>::array_type a(5);
        a[0] = basic_value<preserve_comments>(3, {"comment"});
        a[1] = basic_value<preserve_comments>(1, {"comment"});
        a[2] = basic_value<preserve_comments>(4, {"comment"});
        a[3] = basic_value<preserve_comments>(1, {"comment"});
        a[4] = basic_value<preserve_comments>(5, {"comment"});
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
    }

    {
        typename basic_value<discard_comments>::array_type a(3);
        a[0] = basic_value<discard_comments>("foo");
        a[1] = basic_value<discard_comments>("bar");
        a[2] = basic_value<discard_comments>("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
    }
    {
        typename basic_value<preserve_comments>::array_type a(3);
        a[0] = basic_value<preserve_comments>("foo");
        a[1] = basic_value<preserve_comments>("bar");
        a[2] = basic_value<preserve_comments>("baz");
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
    }

    {
        typename basic_value<discard_comments>::array_type a(3);
        a[0] = basic_value<discard_comments>("foo");
        a[1] = basic_value<discard_comments>("b#r");
        a[2] = basic_value<discard_comments>("b#z");
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
    }
    {
        typename basic_value<preserve_comments>::array_type a(3);
        a[0] = basic_value<preserve_comments>("foo", {"comment"});
        a[1] = basic_value<preserve_comments>("b#r", {"comment"});
        a[2] = basic_value<preserve_comments>("b#z", {"comment"});
        TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
    }
}

BOOST_AUTO_TEST_CASE(test_multiline_array_value)
{
    TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value< discard_comments>>, "[\n#comment\n]", basic_value< discard_comments>(typename basic_value< discard_comments>::array_type()));
    TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\n#comment\n]", basic_value<preserve_comments>(typename basic_value<preserve_comments>::array_type()));

    {
        typename basic_value<discard_comments>::array_type a(5);
        a[0] = basic_value<discard_comments>(3);
        a[1] = basic_value<discard_comments>(1);
        a[2] = basic_value<discard_comments>(4);
        a[3] = basic_value<discard_comments>(1);
        a[4] = basic_value<discard_comments>(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", basic_value<discard_comments>(a));
    }
    {
        typename basic_value<preserve_comments>::array_type a(5);
        a[0] = basic_value<preserve_comments>(3);
        a[1] = basic_value<preserve_comments>(1);
        a[2] = basic_value<preserve_comments>(4);
        a[3] = basic_value<preserve_comments>(1);
        a[4] = basic_value<preserve_comments>(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", basic_value<preserve_comments>(a));
    }

    {
        typename basic_value<discard_comments>::array_type a(5);
        a[0] = basic_value<discard_comments>(3);
        a[1] = basic_value<discard_comments>(1);
        a[2] = basic_value<discard_comments>(4);
        a[3] = basic_value<discard_comments>(1);
        a[4] = basic_value<discard_comments>(5);
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value<discard_comments>(a));
    }
    {
        typename basic_value<preserve_comments>::array_type a(5);
        a[0] = basic_value<preserve_comments>(3, {"comment"});
        a[1] = basic_value<preserve_comments>(1, {"comment"});
        a[2] = basic_value<preserve_comments>(4, {"comment"});
        a[3] = basic_value<preserve_comments>(1, {"comment"});
        a[4] = basic_value<preserve_comments>(5, {"comment"});
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value<preserve_comments>(a));
    }

    {
        typename basic_value<discard_comments>::array_type a(3);
        a[0] = basic_value<discard_comments>("foo");
        a[1] = basic_value<discard_comments>("bar");
        a[2] = basic_value<discard_comments>("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value<discard_comments>(a));
    }
    {
        typename basic_value<preserve_comments>::array_type a(3);
        a[0] = basic_value<preserve_comments>("foo");
        a[1] = basic_value<preserve_comments>("bar");
        a[2] = basic_value<preserve_comments>("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value<preserve_comments>(a));
    }

    {
        typename basic_value<discard_comments>::array_type a(3);
        a[0] = basic_value<discard_comments>("foo");
        a[1] = basic_value<discard_comments>("b#r");
        a[2] = basic_value<discard_comments>("b#z");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value<discard_comments>(a));
    }
    {
        typename basic_value<preserve_comments>::array_type a(3);
        a[0] = basic_value<preserve_comments>("foo", {"comment"});
        a[1] = basic_value<preserve_comments>("b#r", {"comment"});
        a[2] = basic_value<preserve_comments>("b#z", {"comment"});
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value<preserve_comments>(a));
    }

}

BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
{
#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES
    BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed.");
#else
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a));
    }
    {
        array a(5);
        a[0] = toml::value("foo");
        a[1] = toml::value(3.14);
        a[2] = toml::value(42);
        a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)};
        a[4] = toml::value{{"key", "value"}};
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a));
    }
#endif
}

BOOST_AUTO_TEST_CASE(test_comments_after_comma)
{
    {
        typename basic_value<discard_comments>::array_type a(3);
        a[0] = basic_value<discard_comments>("foo");
        a[1] = basic_value<discard_comments>("bar");
        a[2] = basic_value<discard_comments>("baz");
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>,
            "[ \"foo\" # comment\n"
            ", \"bar\" # comment\n"
            ", \"baz\" # comment\n"
            "]", basic_value<discard_comments>(a));
    }

    {
        typename basic_value<preserve_comments>::array_type a(3);
        a[0] = basic_value<preserve_comments>("foo", {" comment"});
        a[1] = basic_value<preserve_comments>("bar", {" comment"});
        a[2] = basic_value<preserve_comments>("baz", {" comment"});
        TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>,
            "[ \"foo\" # comment\n"
            ", \"bar\" # comment\n"
            ", \"baz\" # comment\n"
            "]", basic_value<preserve_comments>(a));
    }

}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "client/client.hpp"
#include "controller.hpp"
#include "export.hpp"
#include "extensions.hpp"
#include "invoker.hpp"
#include "logging.hpp"
#include "shared.hpp"
#include "shared/functions.hpp"

INITIALIZE_EASYLOGGINGPP

#ifndef __linux__
extern "C" DLLEXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
#endif

static char version[] = "1.0";

std::optional<std::string_view> get_command(std::string_view input) {
    size_t cmd_end = input.find(':');
    if (cmd_end < 1) {
        return {};
    }

    return input.substr(0, cmd_end);
}

std::atomic_bool _threaded(false);
#ifdef __linux__
extern "C" void RVExtension(char *output, int outputSize, const char *function) {
#else
void __stdcall RVExtension(char *output, int outputSize, const char *function) {
#endif
    ZERO_OUTPUT();

    // Get the command, then the command args
    std::string_view input = function;

    std::optional<std::string_view> cmd = get_command(input);
    if (!cmd) {
        output[0] = 0x00;
        return;
    }

    std::string_view command = cmd.value();

    std::string argument_str;
    if (command.length() > 1 && input.length() > command.length() + 1) {
        argument_str = input.substr(command.length() + 1, input.length() + 1 - command.length());
    }
    intercept::arguments _args(argument_str);

    std::string result = "-1";
    _threaded = false;

    if (command == "version"sv) {
        result = version;
    } else if (command == "echo"sv) {
        result = function;
    } else if (command == "async"sv) {
        _threaded = true;
        result = "0";
    } else if (command == "stop"sv) {
        _threaded = false;
    }

    if (command == "init_patch"sv) {
        uintptr_t game_state_addr = intercept::loader::find_game_state(reinterpret_cast<uintptr_t>(output) + outputSize);

        //std::cout << "gameState " << std::hex << game_state_addr << "\n";
        intercept::loader::get().do_function_walk(game_state_addr);
        return;
    }

    intercept::controller::get().call(command, _args, result, _threaded);

    if (result.length() > 0) {
        snprintf(output, outputSize, "%s", result.c_str());
    }
    EXTENSION_RETURN();
}

intercept::client_functions intercept::client::host::functions;

void
#ifdef __linux__
__attribute__((constructor))
#endif
    InitLogging() {
    intercept::client::host::functions = intercept::extensions::get().functions;
    auto arg_line = intercept::search::plugin_searcher::get_command_line();
    std::transform(arg_line.begin(), arg_line.end(), arg_line.begin(), ::tolower);

    if (arg_line.find("-interceptdebuglog"sv) != std::string::npos) {
        spdlog::set_level(spdlog::level::debug);
    } else {
        spdlog::set_level(spdlog::level::info);
    }

    spdlog::set_pattern("[%H:%M:%S]-{%l}- %v");
    try {
        auto logfile_it_ = arg_line.find("-interceptlogfile"sv);
        if (logfile_it_ != std::string::npos) {
            auto path_start_ = logfile_it_ + 17; // skip -interceptlogfile

            //trim left
            path_start_ = arg_line.find_first_not_of(" \t\"", path_start_);

            //trim right
            size_t path_end_ = arg_line.find("\"", path_start_); // find ending quotationmark

            if (path_start_ == std::string::npos) {
                logging::logfile = spdlog::rotating_logger_mt("logfile", "logs/intercept_dll.log", 1024000, 3);
            } else {
                std::string _path = arg_line.substr(path_start_, path_end_ - path_start_);
                logging::logfile = spdlog::rotating_logger_mt("logfile", _path, 1024000, 3);
            }
        } else {
            logging::logfile = spdlog::rotating_logger_mt("logfile", "logs/intercept_dll.log", 1024000, 3);
        }

        logging::logfile->flush_on(spdlog::level::debug);
    } catch (const spdlog::spdlog_ex&) {
        spdlog::set_level(spdlog::level::off);
        logging::logfile = spdlog::stdout_logger_mt("Intercept Core");
    }

    LOG(INFO, "Intercept DLL Loaded");
}

void
#ifdef __linux__
__attribute__((destructor))
#endif
CleanupLogging() {
    if (logging::logfile)
        logging::logfile->flush();
    spdlog::drop_all();
}

#ifndef __linux__
BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            InitLogging();
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            CleanupLogging();
            break;
    }
    return TRUE;
}
#endif
<commit_msg>Check if logs directory exists, fixes crash on Linux (#237)<commit_after>#include <stdio.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "client/client.hpp"
#include "controller.hpp"
#include "export.hpp"
#include "extensions.hpp"
#include "invoker.hpp"
#include "logging.hpp"
#include "shared.hpp"
#include "shared/functions.hpp"

INITIALIZE_EASYLOGGINGPP

#ifndef __linux__
extern "C" DLLEXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
#endif

static char version[] = "1.0";

std::optional<std::string_view> get_command(std::string_view input) {
    size_t cmd_end = input.find(':');
    if (cmd_end < 1) {
        return {};
    }

    return input.substr(0, cmd_end);
}

std::atomic_bool _threaded(false);
#ifdef __linux__
extern "C" void RVExtension(char *output, int outputSize, const char *function) {
#else
void __stdcall RVExtension(char *output, int outputSize, const char *function) {
#endif
    ZERO_OUTPUT();

    // Get the command, then the command args
    std::string_view input = function;

    std::optional<std::string_view> cmd = get_command(input);
    if (!cmd) {
        output[0] = 0x00;
        return;
    }

    std::string_view command = cmd.value();

    std::string argument_str;
    if (command.length() > 1 && input.length() > command.length() + 1) {
        argument_str = input.substr(command.length() + 1, input.length() + 1 - command.length());
    }
    intercept::arguments _args(argument_str);

    std::string result = "-1";
    _threaded = false;

    if (command == "version"sv) {
        result = version;
    } else if (command == "echo"sv) {
        result = function;
    } else if (command == "async"sv) {
        _threaded = true;
        result = "0";
    } else if (command == "stop"sv) {
        _threaded = false;
    }

    if (command == "init_patch"sv) {
        uintptr_t game_state_addr = intercept::loader::find_game_state(reinterpret_cast<uintptr_t>(output) + outputSize);

        //std::cout << "gameState " << std::hex << game_state_addr << "\n";
        intercept::loader::get().do_function_walk(game_state_addr);
        return;
    }

    intercept::controller::get().call(command, _args, result, _threaded);

    if (result.length() > 0) {
        snprintf(output, outputSize, "%s", result.c_str());
    }
    EXTENSION_RETURN();
}

intercept::client_functions intercept::client::host::functions;

void
#ifdef __linux__
__attribute__((constructor))
#endif
    InitLogging() {
    intercept::client::host::functions = intercept::extensions::get().functions;
    auto arg_line = intercept::search::plugin_searcher::get_command_line();
    std::transform(arg_line.begin(), arg_line.end(), arg_line.begin(), ::tolower);

    if (arg_line.find("-interceptdebuglog"sv) != std::string::npos) {
        spdlog::set_level(spdlog::level::debug);
    } else {
        spdlog::set_level(spdlog::level::info);
    }

    spdlog::set_pattern("[%H:%M:%S]-{%l}- %v");
    try {
        auto logfile_it_ = arg_line.find("-interceptlogfile"sv);
        if (logfile_it_ != std::string::npos) {
            auto path_start_ = logfile_it_ + 17; // skip -interceptlogfile

            //trim left
            path_start_ = arg_line.find_first_not_of(" \t\"", path_start_);

            //trim right
            size_t path_end_ = arg_line.find("\"", path_start_); // find ending quotationmark

            if (path_start_ == std::string::npos) {
                if (std::filesystem::is_directory("logs")) {
                    logging::logfile = spdlog::rotating_logger_mt("logfile", "logs/intercept_dll.log", 1024000, 3);
                } else {
                    logging::logfile = spdlog::stdout_logger_mt("Intercept Core");
                }
            } else {
                std::string _path = arg_line.substr(path_start_, path_end_ - path_start_);
                logging::logfile = spdlog::rotating_logger_mt("logfile", _path, 1024000, 3);
            }
        } else if (std::filesystem::is_directory("logs")) {
            logging::logfile = spdlog::rotating_logger_mt("logfile", "logs/intercept_dll.log", 1024000, 3);
        } else {
            spdlog::set_level(spdlog::level::off);
            logging::logfile = spdlog::stdout_logger_mt("Intercept Core");
        }

        logging::logfile->flush_on(spdlog::level::debug);
    } catch (const spdlog::spdlog_ex&) {
        spdlog::set_level(spdlog::level::off);
        logging::logfile = spdlog::stdout_logger_mt("Intercept Core");
    }

    LOG(INFO, "Intercept DLL Loaded");
}

void
#ifdef __linux__
__attribute__((destructor))
#endif
CleanupLogging() {
    if (logging::logfile)
        logging::logfile->flush();
    spdlog::drop_all();
}

#ifndef __linux__
BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            InitLogging();
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            CleanupLogging();
            break;
    }
    return TRUE;
}
#endif
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  Class to generate DCS data base entries                                  //
//  Author: Haavard Helstrup                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

// TTimeStamp startTime(2006,10,18,0,0,0,0,kFALSE)
// TTimeStamp endTime(2006,10,19,0,0,0,0,kFALSE)
// Int_t run=2546
// AliDCSGenDB db
// db->SetDefaultStorage("local:///afs/cern.ch/alice/tpctest/AliRoot/HEAD");
// db->SetSpecificStorage("local:///afs/cern.ch/alice/tpctest/Calib/");
// db->Init(run,"TPC/Config/Pressure","TPC/*/*")
// db->MakeCalib("PressureSensor.txt","DCSMap.root",startTime,endTime,firstRun,lastRun,"TPC/Calib/Pressure")

#include "AliDCSGenDB.h"
#include "AliLog.h"

const Int_t kBeamPeriod=2;

ClassImp(AliDCSGenDB)

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB():
   fFirstRun(0),
   fLastRun(0),
   fSpecificStorage(0),
   fDefaultStorage(0),
   fSensor(0),
   fStorLoc(0),
   fMetaData(0),
   fConfTree(0)
//
//  standard constructor
//
{}

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB(const char* defaultStorage, const char* specificStorage):
   fFirstRun(0),
   fLastRun(0),
   fSpecificStorage(specificStorage),
   fDefaultStorage(defaultStorage),
   fSensor(0),
   fStorLoc(0),
   fMetaData(0),
   fConfTree(0)
//
//  special constructor
//
{}

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB(const AliDCSGenDB& org):
  TObject(org),
  fFirstRun(org.fFirstRun),
  fLastRun(org.fLastRun),
  fSpecificStorage(org.fSpecificStorage),
  fDefaultStorage(org.fDefaultStorage),
  fSensor(0),
  fStorLoc(0),
  fMetaData(0),
  fConfTree(0)
{
//
//  Copy constructor
//

 AliError("copy constructor not implemented");

}

//______________________________________________________________________________________________
AliDCSGenDB::~AliDCSGenDB(){
//
// destructor
//
   delete fSensor;
   delete fMetaData;
   delete fConfTree;
}

//______________________________________________________________________________________________
AliDCSGenDB& AliDCSGenDB::operator= (const AliDCSGenDB& /*org*/ )
{
 //
 // assignment operator
 //
 AliError("assignment operator not implemented");
 return *this;

}

//______________________________________________________________________________________________

void AliDCSGenDB::MakeCalib(const char *list, const char *mapDCS,
                             const TTimeStamp& startTime,
			     const TTimeStamp& endTime,
			     Int_t firstRun, Int_t lastRun, const char *calibDir )
{

   // Generate calibration entry from DCS map
   // Configuration read from ASCII file specified by list

   TClonesArray *arr = ReadList(list);
   AliDCSSensorArray *fSensor = new AliDCSSensorArray(arr);
   fSensor->SetStartTime(startTime);
   fSensor->SetEndTime(endTime);
   TMap* map = SetGraphFile(mapDCS);
   if (map) {
     fSensor->MakeSplineFit(map);
   }
   delete map;
   map=0;
   mapDCS=0;

   SetFirstRun(firstRun);
   SetLastRun(lastRun);

   StoreObject(calibDir, fSensor, fMetaData);
}

//______________________________________________________________________________________________
void AliDCSGenDB::MakeConfig(const char *file, Int_t firstRun, Int_t lastRun, const char *confDir )
{
   //
   // Store Configuration file to OCDB
   //

   TTree *tree = ReadListTree(file);
   SetConfTree(tree);
   SetFirstRun(firstRun);
   SetLastRun(lastRun);

   StoreObject(confDir, fConfTree, fMetaData);
}

//______________________________________________________________________________________________
AliCDBMetaData* AliDCSGenDB::CreateMetaObject(const char* objectClassName)
{
  AliCDBMetaData *md1= new AliCDBMetaData();
  md1->SetObjectClassName(objectClassName);
  md1->SetResponsible("Haavard Helstrup");
  md1->SetBeamPeriod(kBeamPeriod);
  md1->SetAliRootVersion(gSystem->Getenv("ARVERSION"));

  return md1;
}

//______________________________________________________________________________________________
void AliDCSGenDB::StoreObject(const char* cdbPath, TObject* object, AliCDBMetaData* metaData)
{

  AliCDBId id1(cdbPath, fFirstRun, fLastRun);
  if (fStorLoc) fStorLoc->Put(object, id1, metaData);
}

//______________________________________________________________________________________________
void AliDCSGenDB::Init(Int_t run, const char *configDir,
                                  const char *specificDir,
				  const char *sensorClass)
{

   fMetaData = CreateMetaObject(sensorClass);
   AliCDBManager *man = AliCDBManager::Instance();
   man->SetDefaultStorage(fDefaultStorage);
   man->SetRun(run);
   man->SetSpecificStorage(specificDir,fSpecificStorage);
   AliCDBEntry *config = man->Get(configDir);
   if (config) fConfTree = (TTree*)config->GetObject();
   fStorLoc = man->GetStorage(fSpecificStorage);
   if (!fStorLoc)    return;

   /*Bool_t cdbCache = */AliCDBManager::Instance()->GetCacheFlag(); // save cache status
   AliCDBManager::Instance()->SetCacheFlag(kTRUE); // activate CDB cache

}

//______________________________________________________________________________________________

//_____________________________________________________________________________
TMap* AliDCSGenDB::SetGraphFile(const char *fname)
{
  //
  // Read DCS maps from file given by fname
  //
  TFile file(fname);
  TMap * map = (TMap*)file.Get("DCSMap");
  return map;
}

//______________________________________________________________________________________________

TClonesArray * AliDCSGenDB::ReadList(const char *fname, const char *title) {
  //
  // read values from ascii file
  //
  TTree* tree = new TTree(title,title);
  tree->ReadFile(fname,"");
  TClonesArray *arr = AliDCSSensor::ReadTree(tree);
  delete tree;
  return arr;
}

//______________________________________________________________________________________________

TTree * AliDCSGenDB::ReadListTree(const char *fname, const char *title) {
  //
  // read values from ascii file
  //
  TTree* tree = new TTree(title,title);
  tree->ReadFile(fname,"");
  TClonesArray *arr = AliDCSSensor::ReadTree(tree);
  arr->Delete();
  delete arr;
  return tree;
}

<commit_msg>Warning removed.<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  Class to generate DCS data base entries                                  //
//  Author: Haavard Helstrup                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

// TTimeStamp startTime(2006,10,18,0,0,0,0,kFALSE)
// TTimeStamp endTime(2006,10,19,0,0,0,0,kFALSE)
// Int_t run=2546
// AliDCSGenDB db
// db->SetDefaultStorage("local:///afs/cern.ch/alice/tpctest/AliRoot/HEAD");
// db->SetSpecificStorage("local:///afs/cern.ch/alice/tpctest/Calib/");
// db->Init(run,"TPC/Config/Pressure","TPC/*/*")
// db->MakeCalib("PressureSensor.txt","DCSMap.root",startTime,endTime,firstRun,lastRun,"TPC/Calib/Pressure")

#include "AliDCSGenDB.h"
#include "AliLog.h"

const Int_t kBeamPeriod=2;

ClassImp(AliDCSGenDB)

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB():
   fFirstRun(0),
   fLastRun(0),
   fSpecificStorage(0),
   fDefaultStorage(0),
   fSensor(0),
   fStorLoc(0),
   fMetaData(0),
   fConfTree(0)
//
//  standard constructor
//
{}

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB(const char* defaultStorage, const char* specificStorage):
   fFirstRun(0),
   fLastRun(0),
   fSpecificStorage(specificStorage),
   fDefaultStorage(defaultStorage),
   fSensor(0),
   fStorLoc(0),
   fMetaData(0),
   fConfTree(0)
//
//  special constructor
//
{}

//______________________________________________________________________________________________

AliDCSGenDB::AliDCSGenDB(const AliDCSGenDB& org):
  TObject(org),
  fFirstRun(org.fFirstRun),
  fLastRun(org.fLastRun),
  fSpecificStorage(org.fSpecificStorage),
  fDefaultStorage(org.fDefaultStorage),
  fSensor(0),
  fStorLoc(0),
  fMetaData(0),
  fConfTree(0)
{
//
//  Copy constructor
//

 AliError("copy constructor not implemented");

}

//______________________________________________________________________________________________
AliDCSGenDB::~AliDCSGenDB(){
//
// destructor
//
   delete fSensor;
   delete fMetaData;
   delete fConfTree;
}

//______________________________________________________________________________________________
AliDCSGenDB& AliDCSGenDB::operator= (const AliDCSGenDB& /*org*/ )
{
 //
 // assignment operator
 //
 AliError("assignment operator not implemented");
 return *this;

}

//______________________________________________________________________________________________

void AliDCSGenDB::MakeCalib(const char *list, const char *mapDCS,
                             const TTimeStamp& startTime,
			     const TTimeStamp& endTime,
			     Int_t firstRun, Int_t lastRun, const char *calibDir )
{

   // Generate calibration entry from DCS map
   // Configuration read from ASCII file specified by list

   TClonesArray *arr = ReadList(list);
   fSensor = new AliDCSSensorArray(arr);
   fSensor->SetStartTime(startTime);
   fSensor->SetEndTime(endTime);
   TMap* map = SetGraphFile(mapDCS);
   if (map) {
     fSensor->MakeSplineFit(map);
   }
   delete map;
   map=0;
   mapDCS=0;

   SetFirstRun(firstRun);
   SetLastRun(lastRun);

   StoreObject(calibDir, fSensor, fMetaData);
}

//______________________________________________________________________________________________
void AliDCSGenDB::MakeConfig(const char *file, Int_t firstRun, Int_t lastRun, const char *confDir )
{
   //
   // Store Configuration file to OCDB
   //

   TTree *tree = ReadListTree(file);
   SetConfTree(tree);
   SetFirstRun(firstRun);
   SetLastRun(lastRun);

   StoreObject(confDir, fConfTree, fMetaData);
}

//______________________________________________________________________________________________
AliCDBMetaData* AliDCSGenDB::CreateMetaObject(const char* objectClassName)
{
  AliCDBMetaData *md1= new AliCDBMetaData();
  md1->SetObjectClassName(objectClassName);
  md1->SetResponsible("Haavard Helstrup");
  md1->SetBeamPeriod(kBeamPeriod);
  md1->SetAliRootVersion(gSystem->Getenv("ARVERSION"));

  return md1;
}

//______________________________________________________________________________________________
void AliDCSGenDB::StoreObject(const char* cdbPath, TObject* object, AliCDBMetaData* metaData)
{

  AliCDBId id1(cdbPath, fFirstRun, fLastRun);
  if (fStorLoc) fStorLoc->Put(object, id1, metaData);
}

//______________________________________________________________________________________________
void AliDCSGenDB::Init(Int_t run, const char *configDir,
                                  const char *specificDir,
				  const char *sensorClass)
{

   fMetaData = CreateMetaObject(sensorClass);
   AliCDBManager *man = AliCDBManager::Instance();
   man->SetDefaultStorage(fDefaultStorage);
   man->SetRun(run);
   man->SetSpecificStorage(specificDir,fSpecificStorage);
   AliCDBEntry *config = man->Get(configDir);
   if (config) fConfTree = (TTree*)config->GetObject();
   fStorLoc = man->GetStorage(fSpecificStorage);
   if (!fStorLoc)    return;

   /*Bool_t cdbCache = */AliCDBManager::Instance()->GetCacheFlag(); // save cache status
   AliCDBManager::Instance()->SetCacheFlag(kTRUE); // activate CDB cache

}

//______________________________________________________________________________________________

//_____________________________________________________________________________
TMap* AliDCSGenDB::SetGraphFile(const char *fname)
{
  //
  // Read DCS maps from file given by fname
  //
  TFile file(fname);
  TMap * map = (TMap*)file.Get("DCSMap");
  return map;
}

//______________________________________________________________________________________________

TClonesArray * AliDCSGenDB::ReadList(const char *fname, const char *title) {
  //
  // read values from ascii file
  //
  TTree* tree = new TTree(title,title);
  tree->ReadFile(fname,"");
  TClonesArray *arr = AliDCSSensor::ReadTree(tree);
  delete tree;
  return arr;
}

//______________________________________________________________________________________________

TTree * AliDCSGenDB::ReadListTree(const char *fname, const char *title) {
  //
  // read values from ascii file
  //
  TTree* tree = new TTree(title,title);
  tree->ReadFile(fname,"");
  TClonesArray *arr = AliDCSSensor::ReadTree(tree);
  arr->Delete();
  delete arr;
  return tree;
}

<|endoftext|>


<commit_before>#include "models/page.h"
#include <QUrl>
#include "functions.h"
#include "logger.h"
#include "models/api/api.h"
#include "models/site.h"

Page::Page(Profile *profile, Site *site, const QList<Site*> &sites, QStringList tags, int page, int limit, const QStringList &postFiltering, bool smart, QObject *parent, int pool, int lastPage, qulonglong lastPageMinId, qulonglong lastPageMaxId)
	: QObject(parent), m_site(site), m_regexApi(-1), m_errors(QStringList()), m_imagesPerPage(limit), m_lastPage(lastPage), m_lastPageMinId(lastPageMinId), m_lastPageMaxId(lastPageMaxId), m_smart(smart)
{
	m_website = m_site->url();
	m_imagesCount = -1;
	m_pagesCount = -1;

	// Replace shortcuts to increase compatibility
	QString text = " " + tags.join(' ') + " ";
	text.replace(" rating:s ", " rating:safe ", Qt::CaseInsensitive)
		.replace(" rating:q ", " rating:questionable ", Qt::CaseInsensitive)
		.replace(" rating:e ", " rating:explicit ", Qt::CaseInsensitive)
		.replace(" -rating:s ", " -rating:safe ", Qt::CaseInsensitive)
		.replace(" -rating:q ", " -rating:questionable ", Qt::CaseInsensitive)
		.replace(" -rating:e ", " -rating:explicit ", Qt::CaseInsensitive);
	tags = text.split(" ", QString::SkipEmptyParts);

	// Get the list of all enabled modifiers
	QStringList modifiers = QStringList();
	for (Site *ste : sites)
	{ modifiers.append(ste->getApis().first()->modifiers()); }
	const QStringList mods = m_site->getApis().first()->modifiers();
	for (const QString &mod : mods)
	{ modifiers.removeAll(mod); }

	// Remove modifiers from tags
	for (const QString &mod : modifiers)
	{ tags.removeAll(mod); }
	m_search = tags;

	// Generate pages
	PostFilter postFilter(postFiltering);
	m_siteApis = m_site->getLoggedInApis();
	m_pageApis.reserve(m_siteApis.count());
	for (Api *api : qAsConst(m_siteApis))
	{
		auto *pageApi = new PageApi(this, profile, m_site, api, m_search, page, limit, postFilter, smart, parent, pool, lastPage, lastPageMinId, lastPageMaxId);
		if (m_pageApis.count() == 0)
		{ connect(pageApi, &PageApi::httpsRedirect, this, &Page::httpsRedirectSlot); }
		m_pageApis.append(pageApi);
		if (api->getName() == QLatin1String("Html") && m_regexApi < 0)
		{ m_regexApi = m_pageApis.count() - 1; }
	}
	m_currentApi = -1;

	// Set values
	m_page = page;
	m_pool = pool;
	fallback(false);
}
Page::~Page()
{
	qDeleteAll(m_pageApis);
}

void Page::fallback(bool loadIfPossible)
{
	m_errors.clear();

	if (m_currentApi >= m_siteApis.count() - 1)
	{
		log(QStringLiteral("[%1] No valid source of the site returned result.").arg(m_site->url()), Logger::Warning);
		m_errors.append(tr("No valid source of the site returned result."));
		emit failedLoading(this);
		return;
	}

	m_currentApi++;
	if (m_currentApi > 0)
		log(QStringLiteral("[%1] Loading using %2 failed. Retry using %3.").arg(m_site->url(), m_siteApis[m_currentApi - 1]->getName(), m_siteApis[m_currentApi]->getName()), Logger::Warning);

	if (loadIfPossible)
		load();
}

void Page::setLastPage(Page *page)
{
	m_lastPage = page->page();
	m_lastPageMaxId = page->maxId();
	m_lastPageMinId = page->minId();

	for (PageApi *api : qAsConst(m_pageApis))
		api->setLastPage(page);

	m_currentApi--;
	if (!page->nextPage().isEmpty())
	{ /*m_url = page->nextPage();*/ }
	else
	{ fallback(false); }
}

void Page::load(bool rateLimit)
{
	connect(m_pageApis[m_currentApi], &PageApi::finishedLoading, this, &Page::loadFinished);
	m_pageApis[m_currentApi]->load(rateLimit);
}
void Page::loadFinished(PageApi *api, PageApi::LoadResult status)
{
	if (api != m_pageApis[m_currentApi])
		return;

	if (status == PageApi::LoadResult::Ok)
		emit finishedLoading(this);
	else
	{
		if (!api->errors().isEmpty())
			m_errors.append(api->errors());
		fallback();
	}
}
void Page::abort()
{
	m_pageApis[m_currentApi]->abort();
}

void Page::loadTags()
{
	if (m_regexApi < 0)
		return;

	connect(m_pageApis[m_regexApi], &PageApi::finishedLoading, this, &Page::loadTagsFinished);
	m_pageApis[m_regexApi]->load();
}
void Page::loadTagsFinished(PageApi *api, PageApi::LoadResult status)
{
	Q_UNUSED(status);

	if (m_regexApi < 0 || api != m_pageApis[m_regexApi])
		return;

	emit finishedLoadingTags(this);
}
void Page::abortTags()
{
	if (m_regexApi < 0)
		return;

	m_pageApis[m_regexApi]->abort();
}

void Page::httpsRedirectSlot()
{ emit httpsRedirect(this); }

void Page::clear()
{
	for (PageApi *pageApi : qAsConst(m_pageApis))
		pageApi->clear();
}

Site *Page::site() const { return m_site; }
const QString &Page::website() const { return m_website; }
const QString &Page::wiki() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->wiki(); }
const QStringList &Page::search() const { return m_search; }
const QStringList &Page::errors() const { return m_errors; }
int Page::imagesPerPage() const { return m_imagesPerPage; }
int Page::page() const { return m_page; }
int Page::pageImageCount() const { return m_pageApis[m_currentApi]->pageImageCount(); }
const QList<QSharedPointer<Image>> &Page::images() const { return m_pageApis[m_currentApi]->images(); }
const QUrl &Page::url() const { return m_pageApis[m_currentApi]->url(); }
const QUrl &Page::friendlyUrl() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->url(); }
const QList<Tag> &Page::tags() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->tags(); }
const QUrl &Page::nextPage() const { return m_pageApis[m_currentApi]->nextPage(); }
const QUrl &Page::prevPage() const { return m_pageApis[m_currentApi]->prevPage(); }
int Page::highLimit() const { return m_pageApis[m_currentApi]->highLimit(); }

bool Page::hasSource() const
{
	for (auto pageApi : qAsConst(m_pageApis))
		if (!pageApi->source().isEmpty())
			return true;
	return false;
}

int Page::imagesCount(bool guess) const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isImageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->imagesCount(guess);
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->imagesCount(guess);
}
int Page::maxImagesCount() const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isImageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->maxImagesCount();
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->maxImagesCount();
}
int Page::pagesCount(bool guess) const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isPageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->pagesCount(guess);
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->pagesCount(guess);
}
int Page::maxPagesCount() const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isPageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->maxPagesCount();
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->maxPagesCount();
}

qulonglong Page::maxId() const
{
	return m_pageApis[m_currentApi]->maxId();
}
qulonglong Page::minId() const
{
	return m_pageApis[m_currentApi]->minId();
}
<commit_msg>Fix crash on sources that provide next page information<commit_after>#include "models/page.h"
#include <QUrl>
#include "functions.h"
#include "logger.h"
#include "models/api/api.h"
#include "models/site.h"

Page::Page(Profile *profile, Site *site, const QList<Site*> &sites, QStringList tags, int page, int limit, const QStringList &postFiltering, bool smart, QObject *parent, int pool, int lastPage, qulonglong lastPageMinId, qulonglong lastPageMaxId)
	: QObject(parent), m_site(site), m_regexApi(-1), m_errors(QStringList()), m_imagesPerPage(limit), m_lastPage(lastPage), m_lastPageMinId(lastPageMinId), m_lastPageMaxId(lastPageMaxId), m_smart(smart)
{
	m_website = m_site->url();
	m_imagesCount = -1;
	m_pagesCount = -1;

	// Replace shortcuts to increase compatibility
	QString text = " " + tags.join(' ') + " ";
	text.replace(" rating:s ", " rating:safe ", Qt::CaseInsensitive)
		.replace(" rating:q ", " rating:questionable ", Qt::CaseInsensitive)
		.replace(" rating:e ", " rating:explicit ", Qt::CaseInsensitive)
		.replace(" -rating:s ", " -rating:safe ", Qt::CaseInsensitive)
		.replace(" -rating:q ", " -rating:questionable ", Qt::CaseInsensitive)
		.replace(" -rating:e ", " -rating:explicit ", Qt::CaseInsensitive);
	tags = text.split(" ", QString::SkipEmptyParts);

	// Get the list of all enabled modifiers
	QStringList modifiers = QStringList();
	for (Site *ste : sites)
	{ modifiers.append(ste->getApis().first()->modifiers()); }
	const QStringList mods = m_site->getApis().first()->modifiers();
	for (const QString &mod : mods)
	{ modifiers.removeAll(mod); }

	// Remove modifiers from tags
	for (const QString &mod : modifiers)
	{ tags.removeAll(mod); }
	m_search = tags;

	// Generate pages
	PostFilter postFilter(postFiltering);
	m_siteApis = m_site->getLoggedInApis();
	m_pageApis.reserve(m_siteApis.count());
	for (Api *api : qAsConst(m_siteApis))
	{
		auto *pageApi = new PageApi(this, profile, m_site, api, m_search, page, limit, postFilter, smart, parent, pool, lastPage, lastPageMinId, lastPageMaxId);
		if (m_pageApis.count() == 0)
		{ connect(pageApi, &PageApi::httpsRedirect, this, &Page::httpsRedirectSlot); }
		m_pageApis.append(pageApi);
		if (api->getName() == QLatin1String("Html") && m_regexApi < 0)
		{ m_regexApi = m_pageApis.count() - 1; }
	}
	m_currentApi = -1;

	// Set values
	m_page = page;
	m_pool = pool;
	fallback(false);
}
Page::~Page()
{
	qDeleteAll(m_pageApis);
}

void Page::fallback(bool loadIfPossible)
{
	m_errors.clear();

	if (m_currentApi >= m_siteApis.count() - 1)
	{
		log(QStringLiteral("[%1] No valid source of the site returned result.").arg(m_site->url()), Logger::Warning);
		m_errors.append(tr("No valid source of the site returned result."));
		emit failedLoading(this);
		return;
	}

	m_currentApi++;
	if (m_currentApi > 0)
		log(QStringLiteral("[%1] Loading using %2 failed. Retry using %3.").arg(m_site->url(), m_siteApis[m_currentApi - 1]->getName(), m_siteApis[m_currentApi]->getName()), Logger::Warning);

	if (loadIfPossible)
		load();
}

void Page::setLastPage(Page *page)
{
	m_lastPage = page->page();
	m_lastPageMaxId = page->maxId();
	m_lastPageMinId = page->minId();

	for (PageApi *api : qAsConst(m_pageApis))
		api->setLastPage(page);

	if (false && !page->nextPage().isEmpty())
	{ /*m_url = page->nextPage();*/ }
	else
	{
		m_currentApi--;
		fallback(false);
	}
}

void Page::load(bool rateLimit)
{
	connect(m_pageApis[m_currentApi], &PageApi::finishedLoading, this, &Page::loadFinished);
	m_pageApis[m_currentApi]->load(rateLimit);
}
void Page::loadFinished(PageApi *api, PageApi::LoadResult status)
{
	if (api != m_pageApis[m_currentApi])
		return;

	if (status == PageApi::LoadResult::Ok)
		emit finishedLoading(this);
	else
	{
		if (!api->errors().isEmpty())
			m_errors.append(api->errors());
		fallback();
	}
}
void Page::abort()
{
	m_pageApis[m_currentApi]->abort();
}

void Page::loadTags()
{
	if (m_regexApi < 0)
		return;

	connect(m_pageApis[m_regexApi], &PageApi::finishedLoading, this, &Page::loadTagsFinished);
	m_pageApis[m_regexApi]->load();
}
void Page::loadTagsFinished(PageApi *api, PageApi::LoadResult status)
{
	Q_UNUSED(status);

	if (m_regexApi < 0 || api != m_pageApis[m_regexApi])
		return;

	emit finishedLoadingTags(this);
}
void Page::abortTags()
{
	if (m_regexApi < 0)
		return;

	m_pageApis[m_regexApi]->abort();
}

void Page::httpsRedirectSlot()
{ emit httpsRedirect(this); }

void Page::clear()
{
	for (PageApi *pageApi : qAsConst(m_pageApis))
		pageApi->clear();
}

Site *Page::site() const { return m_site; }
const QString &Page::website() const { return m_website; }
const QString &Page::wiki() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->wiki(); }
const QStringList &Page::search() const { return m_search; }
const QStringList &Page::errors() const { return m_errors; }
int Page::imagesPerPage() const { return m_imagesPerPage; }
int Page::page() const { return m_page; }
int Page::pageImageCount() const { return m_pageApis[m_currentApi]->pageImageCount(); }
const QList<QSharedPointer<Image>> &Page::images() const { return m_pageApis[m_currentApi]->images(); }
const QUrl &Page::url() const { return m_pageApis[m_currentApi]->url(); }
const QUrl &Page::friendlyUrl() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->url(); }
const QList<Tag> &Page::tags() const { return m_pageApis[m_regexApi < 0 ? m_currentApi : m_regexApi]->tags(); }
const QUrl &Page::nextPage() const { return m_pageApis[m_currentApi]->nextPage(); }
const QUrl &Page::prevPage() const { return m_pageApis[m_currentApi]->prevPage(); }
int Page::highLimit() const { return m_pageApis[m_currentApi]->highLimit(); }

bool Page::hasSource() const
{
	for (auto pageApi : qAsConst(m_pageApis))
		if (!pageApi->source().isEmpty())
			return true;
	return false;
}

int Page::imagesCount(bool guess) const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isImageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->imagesCount(guess);
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->imagesCount(guess);
}
int Page::maxImagesCount() const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isImageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->maxImagesCount();
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->maxImagesCount();
}
int Page::pagesCount(bool guess) const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isPageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->pagesCount(guess);
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->pagesCount(guess);
}
int Page::maxPagesCount() const
{
	if (m_regexApi >= 0 && !m_pageApis[m_currentApi]->isPageCountSure())
	{
		const int count = m_pageApis[m_regexApi]->maxPagesCount();
		if (count >= 0)
			return count;
	}
	return m_pageApis[m_currentApi]->maxPagesCount();
}

qulonglong Page::maxId() const
{
	return m_pageApis[m_currentApi]->maxId();
}
qulonglong Page::minId() const
{
	return m_pageApis[m_currentApi]->minId();
}
<|endoftext|>