<commit_before>//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines CallAndMessageChecker, a builtin checker that checks for various
// errors of call and objc message expressions.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"

using namespace clang;
using namespace ento;

namespace {
class CallAndMessageChecker
  : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > {
  mutable llvm::OwningPtr<BugType> BT_call_null;
  mutable llvm::OwningPtr<BugType> BT_call_undef;
  mutable llvm::OwningPtr<BugType> BT_call_arg;
  mutable llvm::OwningPtr<BugType> BT_msg_undef;
  mutable llvm::OwningPtr<BugType> BT_msg_arg;
  mutable llvm::OwningPtr<BugType> BT_msg_ret;
public:

  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;

private:
  static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg,
                             const char *BT_desc, llvm::OwningPtr<BugType> &BT);
  static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange,
          const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT);

  static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
  void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg,
                          ExplodedNode *N) const;

  void HandleNilReceiver(CheckerContext &C,
                         ProgramStateRef state,
                         ObjCMessage msg) const;

  static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) {
    if (!BT)
      BT.reset(new BuiltinBug(desc));
  }
};
} // end anonymous namespace

void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
                                        const CallExpr *CE) {
  ExplodedNode *N = C.generateSink();
  if (!N)
    return;

  BugReport *R = new BugReport(*BT, BT->getName(), N);
  R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                               bugreporter::GetCalleeExpr(N)));
  C.EmitReport(R);
}

void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C,
                                                CallOrObjCMessage callOrMsg,
                                                const char *BT_desc,
                                                llvm::OwningPtr<BugType> &BT) {
  for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i)
    if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i),
                           callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i),
                           BT_desc, BT))
      return;
}

bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
                                               SVal V, SourceRange argRange,
                                               const Expr *argEx,
                                               const char *BT_desc,
                                               llvm::OwningPtr<BugType> &BT) {

  if (V.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      LazyInit_BT(BT_desc, BT);

      // Generate a report for this bug.
      BugReport *R = new BugReport(*BT, BT->getName(), N);
      R->addRange(argRange);
      if (argEx)
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx));
      C.EmitReport(R);
    }
    return true;
  }

  if (const nonloc::LazyCompoundVal *LV =
        dyn_cast<nonloc::LazyCompoundVal>(&V)) {

    class FindUninitializedField {
    public:
      SmallVector<const FieldDecl *, 10> FieldChain;
    private:
      ASTContext &C;
      StoreManager &StoreMgr;
      MemRegionManager &MrMgr;
      Store store;
    public:
      FindUninitializedField(ASTContext &c, StoreManager &storeMgr,
                             MemRegionManager &mrMgr, Store s)
      : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}

      bool Find(const TypedValueRegion *R) {
        QualType T = R->getValueType();
        if (const RecordType *RT = T->getAsStructureType()) {
          const RecordDecl *RD = RT->getDecl()->getDefinition();
          assert(RD && "Referred record has no definition");
          for (RecordDecl::field_iterator I =
               RD->field_begin(), E = RD->field_end(); I!=E; ++I) {
            const FieldRegion *FR = MrMgr.getFieldRegion(*I, R);
            FieldChain.push_back(*I);
            T = (*I)->getType();
            if (T->getAsStructureType()) {
              if (Find(FR))
                return true;
            }
            else {
              const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
              if (V.isUndef())
                return true;
            }
            FieldChain.pop_back();
          }
        }

        return false;
      }
    };

    const LazyCompoundValData *D = LV->getCVData();
    FindUninitializedField F(C.getASTContext(),
                             C.getState()->getStateManager().getStoreManager(),
                             C.getSValBuilder().getRegionManager(),
                             D->getStore());

    if (F.Find(D->getRegion())) {
      if (ExplodedNode *N = C.generateSink()) {
        LazyInit_BT(BT_desc, BT);
        llvm::SmallString<512> Str;
        llvm::raw_svector_ostream os(Str);
        os << "Passed-by-value struct argument contains uninitialized data";

        if (F.FieldChain.size() == 1)
          os << " (e.g., field: '" << *F.FieldChain[0] << "')";
        else {
          os << " (e.g., via the field chain: '";
          bool first = true;
          for (SmallVectorImpl<const FieldDecl *>::iterator
               DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
            if (first)
              first = false;
            else
              os << '.';
            os << **DI;
          }
          os << "')";
        }

        // Generate a report for this bug.
        BugReport *R = new BugReport(*BT, os.str(), N);
        R->addRange(argRange);

        // FIXME: enhance track back for uninitialized value for arbitrary
        // memregions
        C.EmitReport(R);
      }
      return true;
    }
  }

  return false;
}

void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
                                         CheckerContext &C) const{

  const Expr *Callee = CE->getCallee()->IgnoreParens();
  const LocationContext *LCtx = C.getLocationContext();
  SVal L = C.getState()->getSVal(Callee, LCtx);

  if (L.isUndef()) {
    if (!BT_call_undef)
      BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
                                         "uninitalized pointer value"));
    EmitBadCall(BT_call_undef.get(), C, CE);
    return;
  }

  if (isa<loc::ConcreteInt>(L)) {
    if (!BT_call_null)
      BT_call_null.reset(
        new BuiltinBug("Called function pointer is null (null dereference)"));
    EmitBadCall(BT_call_null.get(), C, CE);
  }

  PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx),
                      "Function call argument is an uninitialized value",
                      BT_call_arg);
}

void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
                                                CheckerContext &C) const {

  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();

  // FIXME: Handle 'super'?
  if (const Expr *receiver = msg.getInstanceReceiver()) {
    SVal recVal = state->getSVal(receiver, LCtx);
    if (recVal.isUndef()) {
      if (ExplodedNode *N = C.generateSink()) {
        if (!BT_msg_undef)
          BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is "
                                            "an uninitialized value"));
        BugReport *R =
          new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
        R->addRange(receiver->getSourceRange());
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                   receiver));
        C.EmitReport(R);
      }
      return;
    } else {
      // Bifurcate the state into nil and non-nil ones.
      DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);

      ProgramStateRef notNilState, nilState;
      llvm::tie(notNilState, nilState) = state->assume(receiverVal);

      // Handle receiver must be nil.
      if (nilState && !notNilState) {
        HandleNilReceiver(C, state, msg);
        return;
      }
    }
  }

  const char *bugDesc = msg.isPropertySetter() ?
                     "Argument for property setter is an uninitialized value"
                   : "Argument in message expression is an uninitialized value";
  // Check for any arguments that are uninitialized/undefined.
  PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx),
                      bugDesc, BT_msg_arg);
}

void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
                                               const ObjCMessage &msg,
                                               ExplodedNode *N) const {

  if (!BT_msg_ret)
    BT_msg_ret.reset(
      new BuiltinBug("Receiver in message expression is "
                     "'nil' and returns a garbage value"));

  llvm::SmallString<200> buf;
  llvm::raw_svector_ostream os(buf);
  os << "The receiver of message '" << msg.getSelector().getAsString()
     << "' is nil and returns a value of type '"
     << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage";

  BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
  if (const Expr *receiver = msg.getInstanceReceiver()) {
    report->addRange(receiver->getSourceRange());
    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                    receiver));
  }
  C.EmitReport(report);
}

static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
  return triple.getVendor() == llvm::Triple::Apple &&
         (!triple.isMacOSXVersionLT(10,5) ||
          triple.getArch() == llvm::Triple::arm ||
          triple.getArch() == llvm::Triple::thumb);
}

void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
                                              ProgramStateRef state,
                                              ObjCMessage msg) const {
  ASTContext &Ctx = C.getASTContext();

  // Check the return type of the message expression.  A message to nil will
  // return different values depending on the return type and the architecture.
  QualType RetTy = msg.getType(Ctx);
  CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
  const LocationContext *LCtx = C.getLocationContext();

  if (CanRetTy->isStructureOrClassType()) {
    // Structure returns are safe since the compiler zeroes them out.
    SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
    C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V));
    return;
  }

  // Other cases: check if sizeof(return type) > sizeof(void*)
  if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
                                  .isConsumedExpr(msg.getOriginExpr())) {
    // Compute: sizeof(void *) and sizeof(return type)
    const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
    const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);

    if (voidPtrSize < returnTypeSize &&
        !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
          (Ctx.FloatTy == CanRetTy ||
           Ctx.DoubleTy == CanRetTy ||
           Ctx.LongDoubleTy == CanRetTy ||
           Ctx.LongLongTy == CanRetTy ||
           Ctx.UnsignedLongLongTy == CanRetTy))) {
      if (ExplodedNode *N = C.generateSink(state))
        emitNilReceiverBug(C, msg, N);
      return;
    }

    // Handle the safe cases where the return value is 0 if the
    // receiver is nil.
    //
    // FIXME: For now take the conservative approach that we only
    // return null values if we *know* that the receiver is nil.
    // This is because we can have surprises like:
    //
    //   ... = [[NSScreens screens] objectAtIndex:0];
    //
    // What can happen is that [... screens] could return nil, but
    // it most likely isn't nil.  We should assume the semantics
    // of this case unless we have *a lot* more knowledge.
    //
    SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
    C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V));
    return;
  }

  C.addTransition(state);
}

void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
  mgr.registerChecker<CallAndMessageChecker>();
}
<commit_msg>Fix an assertion failure in isMacOSXVersionLT for IOS targets.<commit_after>//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines CallAndMessageChecker, a builtin checker that checks for various
// errors of call and objc message expressions.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"

using namespace clang;
using namespace ento;

namespace {
class CallAndMessageChecker
  : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > {
  mutable llvm::OwningPtr<BugType> BT_call_null;
  mutable llvm::OwningPtr<BugType> BT_call_undef;
  mutable llvm::OwningPtr<BugType> BT_call_arg;
  mutable llvm::OwningPtr<BugType> BT_msg_undef;
  mutable llvm::OwningPtr<BugType> BT_msg_arg;
  mutable llvm::OwningPtr<BugType> BT_msg_ret;
public:

  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;

private:
  static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg,
                             const char *BT_desc, llvm::OwningPtr<BugType> &BT);
  static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange,
          const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT);

  static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
  void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg,
                          ExplodedNode *N) const;

  void HandleNilReceiver(CheckerContext &C,
                         ProgramStateRef state,
                         ObjCMessage msg) const;

  static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) {
    if (!BT)
      BT.reset(new BuiltinBug(desc));
  }
};
} // end anonymous namespace

void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
                                        const CallExpr *CE) {
  ExplodedNode *N = C.generateSink();
  if (!N)
    return;

  BugReport *R = new BugReport(*BT, BT->getName(), N);
  R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                               bugreporter::GetCalleeExpr(N)));
  C.EmitReport(R);
}

void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C,
                                                CallOrObjCMessage callOrMsg,
                                                const char *BT_desc,
                                                llvm::OwningPtr<BugType> &BT) {
  for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i)
    if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i),
                           callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i),
                           BT_desc, BT))
      return;
}

bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
                                               SVal V, SourceRange argRange,
                                               const Expr *argEx,
                                               const char *BT_desc,
                                               llvm::OwningPtr<BugType> &BT) {

  if (V.isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      LazyInit_BT(BT_desc, BT);

      // Generate a report for this bug.
      BugReport *R = new BugReport(*BT, BT->getName(), N);
      R->addRange(argRange);
      if (argEx)
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx));
      C.EmitReport(R);
    }
    return true;
  }

  if (const nonloc::LazyCompoundVal *LV =
        dyn_cast<nonloc::LazyCompoundVal>(&V)) {

    class FindUninitializedField {
    public:
      SmallVector<const FieldDecl *, 10> FieldChain;
    private:
      ASTContext &C;
      StoreManager &StoreMgr;
      MemRegionManager &MrMgr;
      Store store;
    public:
      FindUninitializedField(ASTContext &c, StoreManager &storeMgr,
                             MemRegionManager &mrMgr, Store s)
      : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}

      bool Find(const TypedValueRegion *R) {
        QualType T = R->getValueType();
        if (const RecordType *RT = T->getAsStructureType()) {
          const RecordDecl *RD = RT->getDecl()->getDefinition();
          assert(RD && "Referred record has no definition");
          for (RecordDecl::field_iterator I =
               RD->field_begin(), E = RD->field_end(); I!=E; ++I) {
            const FieldRegion *FR = MrMgr.getFieldRegion(*I, R);
            FieldChain.push_back(*I);
            T = (*I)->getType();
            if (T->getAsStructureType()) {
              if (Find(FR))
                return true;
            }
            else {
              const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
              if (V.isUndef())
                return true;
            }
            FieldChain.pop_back();
          }
        }

        return false;
      }
    };

    const LazyCompoundValData *D = LV->getCVData();
    FindUninitializedField F(C.getASTContext(),
                             C.getState()->getStateManager().getStoreManager(),
                             C.getSValBuilder().getRegionManager(),
                             D->getStore());

    if (F.Find(D->getRegion())) {
      if (ExplodedNode *N = C.generateSink()) {
        LazyInit_BT(BT_desc, BT);
        llvm::SmallString<512> Str;
        llvm::raw_svector_ostream os(Str);
        os << "Passed-by-value struct argument contains uninitialized data";

        if (F.FieldChain.size() == 1)
          os << " (e.g., field: '" << *F.FieldChain[0] << "')";
        else {
          os << " (e.g., via the field chain: '";
          bool first = true;
          for (SmallVectorImpl<const FieldDecl *>::iterator
               DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
            if (first)
              first = false;
            else
              os << '.';
            os << **DI;
          }
          os << "')";
        }

        // Generate a report for this bug.
        BugReport *R = new BugReport(*BT, os.str(), N);
        R->addRange(argRange);

        // FIXME: enhance track back for uninitialized value for arbitrary
        // memregions
        C.EmitReport(R);
      }
      return true;
    }
  }

  return false;
}

void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
                                         CheckerContext &C) const{

  const Expr *Callee = CE->getCallee()->IgnoreParens();
  const LocationContext *LCtx = C.getLocationContext();
  SVal L = C.getState()->getSVal(Callee, LCtx);

  if (L.isUndef()) {
    if (!BT_call_undef)
      BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
                                         "uninitalized pointer value"));
    EmitBadCall(BT_call_undef.get(), C, CE);
    return;
  }

  if (isa<loc::ConcreteInt>(L)) {
    if (!BT_call_null)
      BT_call_null.reset(
        new BuiltinBug("Called function pointer is null (null dereference)"));
    EmitBadCall(BT_call_null.get(), C, CE);
  }

  PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx),
                      "Function call argument is an uninitialized value",
                      BT_call_arg);
}

void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
                                                CheckerContext &C) const {

  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();

  // FIXME: Handle 'super'?
  if (const Expr *receiver = msg.getInstanceReceiver()) {
    SVal recVal = state->getSVal(receiver, LCtx);
    if (recVal.isUndef()) {
      if (ExplodedNode *N = C.generateSink()) {
        if (!BT_msg_undef)
          BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is "
                                            "an uninitialized value"));
        BugReport *R =
          new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
        R->addRange(receiver->getSourceRange());
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                   receiver));
        C.EmitReport(R);
      }
      return;
    } else {
      // Bifurcate the state into nil and non-nil ones.
      DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);

      ProgramStateRef notNilState, nilState;
      llvm::tie(notNilState, nilState) = state->assume(receiverVal);

      // Handle receiver must be nil.
      if (nilState && !notNilState) {
        HandleNilReceiver(C, state, msg);
        return;
      }
    }
  }

  const char *bugDesc = msg.isPropertySetter() ?
                     "Argument for property setter is an uninitialized value"
                   : "Argument in message expression is an uninitialized value";
  // Check for any arguments that are uninitialized/undefined.
  PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx),
                      bugDesc, BT_msg_arg);
}

void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
                                               const ObjCMessage &msg,
                                               ExplodedNode *N) const {

  if (!BT_msg_ret)
    BT_msg_ret.reset(
      new BuiltinBug("Receiver in message expression is "
                     "'nil' and returns a garbage value"));

  llvm::SmallString<200> buf;
  llvm::raw_svector_ostream os(buf);
  os << "The receiver of message '" << msg.getSelector().getAsString()
     << "' is nil and returns a value of type '"
     << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage";

  BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
  if (const Expr *receiver = msg.getInstanceReceiver()) {
    report->addRange(receiver->getSourceRange());
    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                    receiver));
  }
  C.EmitReport(report);
}

static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
  return (triple.getVendor() == llvm::Triple::Apple &&
          (triple.getOS() == llvm::Triple::IOS ||
           !triple.isMacOSXVersionLT(10,5)));
}

void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
                                              ProgramStateRef state,
                                              ObjCMessage msg) const {
  ASTContext &Ctx = C.getASTContext();

  // Check the return type of the message expression.  A message to nil will
  // return different values depending on the return type and the architecture.
  QualType RetTy = msg.getType(Ctx);
  CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
  const LocationContext *LCtx = C.getLocationContext();

  if (CanRetTy->isStructureOrClassType()) {
    // Structure returns are safe since the compiler zeroes them out.
    SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
    C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V));
    return;
  }

  // Other cases: check if sizeof(return type) > sizeof(void*)
  if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
                                  .isConsumedExpr(msg.getOriginExpr())) {
    // Compute: sizeof(void *) and sizeof(return type)
    const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
    const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);

    if (voidPtrSize < returnTypeSize &&
        !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
          (Ctx.FloatTy == CanRetTy ||
           Ctx.DoubleTy == CanRetTy ||
           Ctx.LongDoubleTy == CanRetTy ||
           Ctx.LongLongTy == CanRetTy ||
           Ctx.UnsignedLongLongTy == CanRetTy))) {
      if (ExplodedNode *N = C.generateSink(state))
        emitNilReceiverBug(C, msg, N);
      return;
    }

    // Handle the safe cases where the return value is 0 if the
    // receiver is nil.
    //
    // FIXME: For now take the conservative approach that we only
    // return null values if we *know* that the receiver is nil.
    // This is because we can have surprises like:
    //
    //   ... = [[NSScreens screens] objectAtIndex:0];
    //
    // What can happen is that [... screens] could return nil, but
    // it most likely isn't nil.  We should assume the semantics
    // of this case unless we have *a lot* more knowledge.
    //
    SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx));
    C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V));
    return;
  }

  C.addTransition(state);
}

void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
  mgr.registerChecker<CallAndMessageChecker>();
}
<|endoftext|>


<commit_before>//
// Created by Scott Stark on 4/7/15.
//

#include "LcdDisplay.h"
#include <wiringPi.h>
#include <lcd.h>

void LcdDisplay::displayBeacon(const Beacon &beacon) {
    char tmp[80];
    sprintf(tmp, "Beacon(%d), rssi=%d", beacon.getMinor(), beacon.getRssi());
    lcdPosition(lcdHandle, 0, 0) ;
    lcdPuts(lcdHandle, tmp);
    sprintf(tmp, "Hello Scott");
    lcdPosition(lcdHandle, 0, 1) ;
    lcdPuts(lcdHandle, tmp) ;
}

void LcdDisplay::displayText(const string &text, int col, int row) {
    lcdPosition(lcdHandle, col, row);
    lcdPuts(lcdHandle, text.c_str());
    // Clear the rest of the line
    size_t length = text.size();
    for(int c = length; c < nCols; c++) {
        lcdPutchar(lcdHandle, ' ');
    }
}

int LcdDisplay::init(int rows, int cols) {
    nCols = cols;
    wiringPiSetup () ;

    lcdHandle = lcdInit (rows, cols, 4, 11,10, 4,5,6,7,0,0,0,0) ;
    if (lcdHandle < 0)
    {
        fprintf (stderr, "lcdInit failed\n") ;
        return -1 ;
    }
    return 0;
}

void LcdDisplay::clear() {
    lcdClear(lcdHandle);
}
<commit_msg>Expand the beacon display information<commit_after>//
// Created by Scott Stark on 4/7/15.
//

#include "LcdDisplay.h"
#include <wiringPi.h>
#include <lcd.h>

void LcdDisplay::displayBeacon(const Beacon &beacon) {
    char tmp[80];
    sprintf(tmp, "Beacon(%d):, rssi=%d", beacon.getMinor(), beacon.getRssi());
    lcdPosition(lcdHandle, 0, 0) ;
    lcdPuts(lcdHandle, tmp);
    sprintf(tmp, "rssi=%d, time=%d", beacon.getRssi(), beacon.getTime());
    lcdPosition(lcdHandle, 2, 1) ;
    lcdPuts(lcdHandle, tmp) ;
    sprintf(tmp, "Hello Scott");
    lcdPosition(lcdHandle, 2, 2) ;
    lcdPuts(lcdHandle, tmp) ;
}

void LcdDisplay::displayText(const string &text, int col, int row) {
    lcdPosition(lcdHandle, col, row);
    lcdPuts(lcdHandle, text.c_str());
    // Clear the rest of the line
    size_t length = text.size();
    for(int c = length; c < nCols; c++) {
        lcdPutchar(lcdHandle, ' ');
    }
}

int LcdDisplay::init(int rows, int cols) {
    nCols = cols;
    wiringPiSetup () ;

    lcdHandle = lcdInit (rows, cols, 4, 11,10, 4,5,6,7,0,0,0,0) ;
    if (lcdHandle < 0)
    {
        fprintf (stderr, "lcdInit failed\n") ;
        return -1 ;
    }
    return 0;
}

void LcdDisplay::clear() {
    lcdClear(lcdHandle);
}
<|endoftext|>


<commit_before>#pragma once

#include <memory>
#include <vector>

#include <boost/thread/tss.hpp>

#include "blackhole/attribute.hpp"
#include "blackhole/detail/config/atomic.hpp"
#include "blackhole/detail/config/noncopyable.hpp"
#include "blackhole/detail/config/nullptr.hpp"
#include "error/handler.hpp"
#include "filter.hpp"
#include "frontend.hpp"
#include "keyword.hpp"
#include "keyword/message.hpp"
#include "keyword/severity.hpp"
#include "keyword/thread.hpp"
#include "keyword/timestamp.hpp"
#include "keyword/tracebit.hpp"
#include "utils/unique.hpp"

#include "blackhole/config.hpp"

namespace blackhole {

class scoped_attributes_concept_t;

template <typename Level>
struct logger_verbosity_traits {
    typedef Level level_type;

    static
    bool
    passed(level_type logger_verbosity, level_type record_verbosity) {
        typedef typename aux::underlying_type<Level>::type underlying_type;

        return static_cast<underlying_type>(record_verbosity) >=
            static_cast<underlying_type>(logger_verbosity);
    }
};

class logger_base_t {
protected:
    typedef boost::shared_mutex rw_mutex_type;
    typedef boost::shared_lock<rw_mutex_type> reader_lock_type;
    typedef boost::unique_lock<rw_mutex_type> writer_lock_type;

    struct state_t {
        std::atomic<bool> enabled;
        std::atomic<bool> tracked;

        filter_t filter;
        struct attrbutes_t {
            attribute::set_t global;
            boost::thread_specific_ptr<scoped_attributes_concept_t> scoped;

            attrbutes_t(void(*deleter)(scoped_attributes_concept_t*)) :
                scoped(deleter)
            {}
        } attributes;

        std::vector<std::unique_ptr<base_frontend_t>> frontends;
        log::exception_handler_t exception;

        struct {
            mutable rw_mutex_type open;
            mutable rw_mutex_type push;
        } lock;

        state_t();
    };
    state_t state;

    friend class scoped_attributes_concept_t;

    friend void swap(logger_base_t& lhs, logger_base_t& rhs) BLACKHOLE_NOEXCEPT;

public:
    logger_base_t();

    //! @compat GCC4.4
    //! Blaming GCC4.4 - it needs explicit move constructor definition,
    //! because it cannot define default move constructor for derived class.
    logger_base_t(logger_base_t&& other) BLACKHOLE_NOEXCEPT;
    logger_base_t& operator=(logger_base_t&& other) BLACKHOLE_NOEXCEPT;

    bool enabled() const;
    void enabled(bool enable);

    bool tracked() const;
    void tracked(bool enable);

    void set_filter(filter_t&& filter);
    void add_attribute(const attribute::pair_t& attribute);
    void add_frontend(std::unique_ptr<base_frontend_t> frontend);
    void set_exception_handler(log::exception_handler_t&& handler);

    record_t open_record() const;
    record_t open_record(attribute::pair_t attribute) const;
    record_t open_record(attribute::set_t attributes) const;

    void push(record_t&& record) const;

private:
    attribute::set_t get_event_attributes() const;
    attribute::set_t get_thread_attributes() const;
};

//!@todo: Develop ImmutableLogger class, which provides almost immutable
//!       internal state (filter, exception handler, frontends).
//!       This class won't require any synchronization mechanizm.

// NOTE: It's not movable to avoid moving to another thread.
class scoped_attributes_concept_t {
    BLACKHOLE_DECLARE_NONCOPYABLE(scoped_attributes_concept_t);

    logger_base_t *m_logger;
    scoped_attributes_concept_t *m_previous;

    friend void swap(logger_base_t&, logger_base_t&) BLACKHOLE_NOEXCEPT;

public:
    scoped_attributes_concept_t(logger_base_t& log);
    virtual ~scoped_attributes_concept_t();

    virtual const attribute::set_t& attributes() const = 0;

protected:
    bool has_parent() const;
    const scoped_attributes_concept_t& parent() const;
};

template<typename Level>
class verbose_logger_t : public logger_base_t {
public:
    typedef Level level_type;

private:
    level_type level;

public:
    verbose_logger_t() :
        logger_base_t(),
        level(static_cast<level_type>(0))
    {}

    //! @compat: GCC4.4
    //! GCC 4.4 doesn't create default copy/move constructor for derived
    //! classes. It's a bug.
    verbose_logger_t(verbose_logger_t&& other) BLACKHOLE_NOEXCEPT :
        logger_base_t(std::move(other)),
        level(static_cast<level_type>(0))
    {}

    verbose_logger_t& operator=(verbose_logger_t&& other) BLACKHOLE_NOEXCEPT {
        logger_base_t::operator=(std::move(other));
        level = other.level;
        return *this;
    }

    // Explicit import other overloaded methods.
    using logger_base_t::open_record;

    /*!
     * Gets the current upper verbosity bound.
     */
    level_type verbosity() const {
        return level;
    }

    /*!
     * Sets the upper verbosity bound.
     * Every log event with a verbosity less than `level` will be dropped.
     * @param[in] level - Upper verbosity value.
     */
    void verbosity(level_type level) {
        this->level = level;
    }

    /*! @todo: Documentation is @deprecated.
     * Tries to open log record with specific verbosity level.
     * Internally this method compares desired verbosity level with the upper
     * one. Can return invalid log record if some conditions are not met.
     * @param[in] level - Desired verbosity level.
     * @return valid or invalid `record_t` object.
     */
    record_t
    open_record(level_type level,
                attribute::set_t local = attribute::set_t()) const
    {
        typedef logger_verbosity_traits<level_type> verbosity_traits;
        const bool passed = verbosity_traits::passed(this->level, level);

        bool trace = false;
        if (!passed) {
            auto it = local.find(keyword::tracebit().name());
            if (it != local.end()) {
                trace = boost::get<keyword::tag::tracebit_t::type>(it->second.value);
            } else {
                reader_lock_type lock(state.lock.open);
                if (state.attributes.scoped.get()) {
                    const auto& scoped = state.attributes.scoped->attributes();
                    auto scoped_it = scoped.find(keyword::tracebit().name());
                    if (scoped_it != scoped.end()) {
                        trace = boost::get<keyword::tag::tracebit_t::type>(
                            scoped_it->second.value
                        );
                    }
                }
            }
        }

        if (passed || trace) {
            local.insert(keyword::severity<Level>() = level);
            return logger_base_t::open_record(std::move(local));
        }

        return record_t();
    }
};

} // namespace blackhole

#if defined(BLACKHOLE_HEADER_ONLY)
#include "blackhole/logger.ipp"
#endif
<commit_msg>[Tasks] One task more.<commit_after>#pragma once

#include <memory>
#include <vector>

#include <boost/thread/tss.hpp>

#include "blackhole/attribute.hpp"
#include "blackhole/detail/config/atomic.hpp"
#include "blackhole/detail/config/noncopyable.hpp"
#include "blackhole/detail/config/nullptr.hpp"
#include "error/handler.hpp"
#include "filter.hpp"
#include "frontend.hpp"
#include "keyword.hpp"
#include "keyword/message.hpp"
#include "keyword/severity.hpp"
#include "keyword/thread.hpp"
#include "keyword/timestamp.hpp"
#include "keyword/tracebit.hpp"
#include "utils/unique.hpp"

#include "blackhole/config.hpp"

namespace blackhole {

class scoped_attributes_concept_t;

template <typename Level>
struct logger_verbosity_traits {
    typedef Level level_type;

    static
    bool
    passed(level_type logger_verbosity, level_type record_verbosity) {
        typedef typename aux::underlying_type<Level>::type underlying_type;

        return static_cast<underlying_type>(record_verbosity) >=
            static_cast<underlying_type>(logger_verbosity);
    }
};

class logger_base_t {
protected:
    typedef boost::shared_mutex rw_mutex_type;
    typedef boost::shared_lock<rw_mutex_type> reader_lock_type;
    typedef boost::unique_lock<rw_mutex_type> writer_lock_type;

    struct state_t {
        std::atomic<bool> enabled;
        std::atomic<bool> tracked;

        filter_t filter;
        struct attrbutes_t {
            attribute::set_t global;
            boost::thread_specific_ptr<scoped_attributes_concept_t> scoped;

            attrbutes_t(void(*deleter)(scoped_attributes_concept_t*)) :
                scoped(deleter)
            {}
        } attributes;

        std::vector<std::unique_ptr<base_frontend_t>> frontends;
        log::exception_handler_t exception;

        struct {
            mutable rw_mutex_type open;
            mutable rw_mutex_type push;
        } lock;

        state_t();
    };
    state_t state;

    friend class scoped_attributes_concept_t;

    friend void swap(logger_base_t& lhs, logger_base_t& rhs) BLACKHOLE_NOEXCEPT;

public:
    logger_base_t();

    //! @compat GCC4.4
    //! Blaming GCC4.4 - it needs explicit move constructor definition,
    //! because it cannot define default move constructor for derived class.
    logger_base_t(logger_base_t&& other) BLACKHOLE_NOEXCEPT;
    logger_base_t& operator=(logger_base_t&& other) BLACKHOLE_NOEXCEPT;

    bool enabled() const;
    void enabled(bool enable);

    bool tracked() const;
    void tracked(bool enable);

    void set_filter(filter_t&& filter);
    void add_attribute(const attribute::pair_t& attribute);
    void add_frontend(std::unique_ptr<base_frontend_t> frontend);
    void set_exception_handler(log::exception_handler_t&& handler);

    record_t open_record() const;
    record_t open_record(attribute::pair_t attribute) const;
    record_t open_record(attribute::set_t attributes) const;

    void push(record_t&& record) const;

private:
    attribute::set_t get_event_attributes() const;
    attribute::set_t get_thread_attributes() const;
};

//!@todo: Develop ImmutableLogger class, which provides almost immutable
//!       internal state (filter, exception handler, frontends).
//!       This class won't require any synchronization mechanizm.

// NOTE: It's not movable to avoid moving to another thread.
class scoped_attributes_concept_t {
    BLACKHOLE_DECLARE_NONCOPYABLE(scoped_attributes_concept_t);

    logger_base_t *m_logger;
    scoped_attributes_concept_t *m_previous;

    friend void swap(logger_base_t&, logger_base_t&) BLACKHOLE_NOEXCEPT;

public:
    scoped_attributes_concept_t(logger_base_t& log);
    virtual ~scoped_attributes_concept_t();

    virtual const attribute::set_t& attributes() const = 0;

protected:
    bool has_parent() const;
    const scoped_attributes_concept_t& parent() const;
};

template<typename Level>
class verbose_logger_t : public logger_base_t {
public:
    typedef Level level_type;

private:
    level_type level;

public:
    verbose_logger_t() :
        logger_base_t(),
        level(static_cast<level_type>(0))
    {}

    //! @compat: GCC4.4
    //! GCC 4.4 doesn't create default copy/move constructor for derived
    //! classes. It's a bug.
    verbose_logger_t(verbose_logger_t&& other) BLACKHOLE_NOEXCEPT :
        logger_base_t(std::move(other)),
        level(static_cast<level_type>(0))
    {}

    verbose_logger_t& operator=(verbose_logger_t&& other) BLACKHOLE_NOEXCEPT {
        logger_base_t::operator=(std::move(other));
        level = other.level;
        return *this;
    }

    // Explicit import other overloaded methods.
    using logger_base_t::open_record;

    /*!
     * Gets the current upper verbosity bound.
     */
    level_type verbosity() const {
        return level;
    }

    /*!
     * Sets the upper verbosity bound.
     * Every log event with a verbosity less than `level` will be dropped.
     * @param[in] level - Upper verbosity value.
     */
    void verbosity(level_type level) {
        this->level = level;
    }

    /*! @todo: Documentation is @deprecated.
     * Tries to open log record with specific verbosity level.
     * Internally this method compares desired verbosity level with the upper
     * one. Can return invalid log record if some conditions are not met.
     * @param[in] level - Desired verbosity level.
     * @return valid or invalid `record_t` object.
     * @todo: Shitcode. Need to fix.
     */
    record_t
    open_record(level_type level,
                attribute::set_t local = attribute::set_t()) const
    {
        typedef logger_verbosity_traits<level_type> verbosity_traits;
        const bool passed = verbosity_traits::passed(this->level, level);

        bool trace = false;
        if (!passed) {
            auto it = local.find(keyword::tracebit().name());
            if (it != local.end()) {
                trace = boost::get<keyword::tag::tracebit_t::type>(it->second.value);
            } else {
                reader_lock_type lock(state.lock.open);
                if (state.attributes.scoped.get()) {
                    const auto& scoped = state.attributes.scoped->attributes();
                    auto scoped_it = scoped.find(keyword::tracebit().name());
                    if (scoped_it != scoped.end()) {
                        trace = boost::get<keyword::tag::tracebit_t::type>(
                            scoped_it->second.value
                        );
                    }
                }
            }
        }

        if (passed || trace) {
            local.insert(keyword::severity<Level>() = level);
            return logger_base_t::open_record(std::move(local));
        }

        return record_t();
    }
};

} // namespace blackhole

#if defined(BLACKHOLE_HEADER_ONLY)
#include "blackhole/logger.ipp"
#endif
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2022, Timothy Stack
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither the name of Timothy Stack nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "breadcrumb_curses.hh"

#include "base/itertools.hh"
#include "itertools.similar.hh"
#include "log_format_fwd.hh"
#include "logfile.hh"

using namespace lnav::roles::literals;

breadcrumb_curses::breadcrumb_curses()
{
    this->bc_match_search_overlay.sos_parent = this;
    this->bc_match_source.set_reverse_selection(true);
    this->bc_match_view.set_selectable(true);
    this->bc_match_view.set_overlay_source(&this->bc_match_search_overlay);
    this->bc_match_view.set_sub_source(&this->bc_match_source);
    this->bc_match_view.set_height(0_vl);
    this->bc_match_view.set_show_scrollbar(true);
    this->bc_match_view.set_default_role(role_t::VCR_POPUP);
    this->add_child_view(&this->bc_match_view);
}

void
breadcrumb_curses::do_update()
{
    if (!this->bc_line_source) {
        return;
    }

    size_t crumb_index = 0;
    size_t sel_crumb_offset = 0;
    auto width = getmaxx(this->bc_window);
    auto crumbs = this->bc_focused_crumbs.empty() ? this->bc_line_source()
                                                  : this->bc_focused_crumbs;
    auto crumbs_line
        = crumbs | lnav::itertools::map(&breadcrumb::crumb::c_display_value)
        | lnav::itertools::fold(
              [&](const auto& elem, auto& accum) {
                  auto accum_width = utf8_string_length(accum.get_string())
                                         .template unwrap();
                  auto elem_width
                      = utf8_string_length(elem.get_string()).template unwrap();
                  auto is_selected = this->bc_selected_crumb
                      && (crumb_index == this->bc_selected_crumb.value());

                  if (is_selected && ((accum_width + elem_width) > width)) {
                      accum.clear();
                      accum.append("\u22ef\u276d"_breadcrumb);
                      accum_width = 2;
                  }

                  accum.append(elem);
                  if (is_selected) {
                      sel_crumb_offset = accum_width;
                      accum.get_attrs().emplace_back(
                          line_range{
                              (int) (accum.length() - elem.length()),
                              (int) accum.length(),
                          },
                          VC_STYLE.template value(A_REVERSE));
                  }
                  crumb_index += 1;
                  return accum.append("\u276d"_breadcrumb);
              },
              attr_line_t());

    line_range lr{0, width};
    view_curses::mvwattrline(
        this->bc_window, this->bc_y, 0, crumbs_line, lr, role_t::VCR_STATUS);

    if (this->bc_selected_crumb) {
        this->bc_match_view.set_x(sel_crumb_offset);
    }
    view_curses::do_update();
}

void
breadcrumb_curses::reload_data()
{
    if (!this->bc_selected_crumb) {
        return;
    }

    auto& selected_crumb_ref
        = this->bc_focused_crumbs[this->bc_selected_crumb.value()];
    this->bc_possible_values = selected_crumb_ref.c_possibility_provider()
        | lnav::itertools::sort_by(&breadcrumb::possibility::p_key);

    nonstd::optional<size_t> selected_value;
    this->bc_similar_values = this->bc_possible_values
        | lnav::itertools::similar_to(
                                  [](const auto& elem) { return elem.p_key; },
                                  this->bc_current_search,
                                  INT_MAX);
    if (selected_crumb_ref.c_key.is<std::string>()) {
        auto& selected_crumb_key = selected_crumb_ref.c_key.get<std::string>();
        auto found_poss_opt = this->bc_similar_values
            | lnav::itertools::find_if([&selected_crumb_key](const auto& elem) {
                                  return elem.p_key == selected_crumb_key;
                              });

        if (found_poss_opt) {
            selected_value = std::distance(this->bc_similar_values.begin(),
                                           found_poss_opt.value());
        } else {
            selected_value = 0;
        }
    }

    auto matches = attr_line_t().join(
        this->bc_similar_values
            | lnav::itertools::map(&breadcrumb::possibility::p_display_value),
        "\n");
    this->bc_match_source.replace_with(matches);
    auto width = this->bc_possible_values
        | lnav::itertools::fold(
                     [](const auto& match, auto& accum) {
                         auto mlen = match.p_display_value.length();
                         if (mlen > accum) {
                             return mlen;
                         }
                         return accum;
                     },
                     selected_crumb_ref.c_display_value.length());

    if (selected_crumb_ref.c_search_placeholder.size() > width) {
        width = selected_crumb_ref.c_search_placeholder.size();
    }
    this->bc_match_view.set_height(vis_line_t(
        std::min(this->bc_match_source.get_lines().size() + 1, size_t{4})));
    this->bc_match_view.set_width(width + 3);
    this->bc_match_view.set_needs_update();
    if (selected_value) {
        this->bc_match_view.set_selection(vis_line_t(selected_value.value()));
        this->bc_match_view.scroll_selection_into_view();
    }
    this->bc_match_view.reload_data();
    this->set_needs_update();
}

void
breadcrumb_curses::focus()
{
    this->bc_focused_crumbs = this->bc_line_source();
    if (this->bc_focused_crumbs.empty()) {
        return;
    }

    this->bc_current_search.clear();
    this->bc_selected_crumb = this->bc_last_selected_crumb.value_or(0);
    this->reload_data();
}

void
breadcrumb_curses::blur()
{
    this->bc_last_selected_crumb = this->bc_selected_crumb;
    this->bc_focused_crumbs.clear();
    this->bc_selected_crumb = nonstd::nullopt;
    this->bc_current_search.clear();
    this->bc_match_view.set_height(0_vl);
    this->bc_match_source.clear();
    this->set_needs_update();
}

bool
breadcrumb_curses::handle_key(int ch)
{
    bool retval = false;

    switch (ch) {
        case KEY_BTAB:
        case KEY_LEFT:
            if (this->bc_selected_crumb) {
                if (this->bc_selected_crumb.value() > 0) {
                    this->bc_selected_crumb
                        = this->bc_selected_crumb.value() - 1;
                } else {
                    this->bc_selected_crumb
                        = this->bc_focused_crumbs.size() - 1;
                }
                this->bc_current_search.clear();
                this->reload_data();
            }
            retval = true;
            break;
        case '\t':
        case KEY_RIGHT:
            if (this->bc_selected_crumb) {
                if (this->bc_selected_crumb.value()
                    < this->bc_focused_crumbs.size() - 1) {
                    this->bc_selected_crumb
                        = this->bc_selected_crumb.value() + 1;
                } else {
                    this->bc_selected_crumb = 0;
                }
                this->bc_current_search.clear();
                this->reload_data();
            }
            retval = true;
            break;
        case KEY_HOME:
            this->bc_match_view.set_selection(0_vl);
            retval = true;
            break;
        case KEY_END:
            this->bc_match_view.set_selection(
                this->bc_match_view.get_inner_height() - 1_vl);
            retval = true;
            break;
        case KEY_NPAGE:
            this->bc_match_view.shift_selection(3);
            retval = true;
            break;
        case KEY_PPAGE:
            this->bc_match_view.shift_selection(-3);
            retval = true;
            break;
        case KEY_UP:
            this->bc_match_view.shift_selection(-1);
            retval = true;
            break;
        case KEY_DOWN:
            this->bc_match_view.shift_selection(1);
            retval = true;
            break;
        case 0x7f:
        case KEY_BACKSPACE:
            if (!this->bc_current_search.empty()) {
                this->bc_current_search.pop_back();
                this->reload_data();
            }
            retval = true;
            break;
        case KEY_ENTER:
        case '\r':
            if (this->bc_selected_crumb) {
                auto& selected_crumb_ref
                    = this->bc_focused_crumbs[this->bc_selected_crumb.value()];
                if (this->bc_match_view.get_selection() >= 0
                    && this->bc_match_view.get_selection()
                        < this->bc_similar_values.size())
                {
                    const auto& new_value
                        = this->bc_similar_values[this->bc_match_view
                                                      .get_selection()]
                              .p_key;

                    selected_crumb_ref.c_performer(new_value);
                } else if (!this->bc_current_search.empty()) {
                    if (selected_crumb_ref.c_possible_range) {
                        size_t index;

                        if (sscanf(
                                this->bc_current_search.c_str(), "%zu", &index)
                            == 1) {
                            selected_crumb_ref.c_performer(index);
                        }
                    } else {
                        selected_crumb_ref.c_performer(this->bc_current_search);
                    }
                }
            }
            break;
        default:
            if (isprint(ch)) {
                this->bc_current_search.push_back(ch);
                this->reload_data();
                retval = true;
            }
            break;
    }

    if (!retval) {
        this->blur();
    }
    this->set_needs_update();
    return retval;
}

bool
breadcrumb_curses::search_overlay_source::list_value_for_overlay(
    const listview_curses& lv,
    int y,
    int bottom,
    vis_line_t line,
    attr_line_t& value_out)
{
    if (y == 0) {
        auto* parent = this->sos_parent;
        auto sel_opt = parent->bc_focused_crumbs
            | lnav::itertools::nth(parent->bc_selected_crumb);
        auto exp_input = sel_opt
            | lnav::itertools::map(&breadcrumb::crumb::c_expected_input)
            | lnav::itertools::unwrap_or(
                             breadcrumb::crumb::expected_input_t::exact);

        value_out.with_attr_for_all(VC_STYLE.value(A_UNDERLINE));

        if (!parent->bc_current_search.empty()) {
            value_out = parent->bc_current_search;

            role_t combobox_role = role_t::VCR_STATUS;
            switch (exp_input) {
                case breadcrumb::crumb::expected_input_t::exact:
                    if (parent->bc_similar_values.empty()) {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                case breadcrumb::crumb::expected_input_t::index: {
                    size_t index;

                    if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
                            != 1
                        || index < 0
                        || (index
                            >= (sel_opt
                                | lnav::itertools::map([](const auto& cr) {
                                      return cr->c_possible_range.value_or(0);
                                  })
                                | lnav::itertools::unwrap_or(size_t{0}))))
                    {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                }
                case breadcrumb::crumb::expected_input_t::index_or_exact: {
                    size_t index;

                    if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
                        == 1) {
                        if (index < 0
                            || (index
                                >= (sel_opt
                                    | lnav::itertools::map([](const auto& cr) {
                                          return cr->c_possible_range.value_or(
                                              0);
                                      })
                                    | lnav::itertools::unwrap_or(size_t{0}))))
                        {
                            combobox_role = role_t::VCR_ALERT_STATUS;
                        }
                    } else if (parent->bc_similar_values.empty()) {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                }
                case breadcrumb::crumb::expected_input_t::anything:
                    break;
            }
            value_out.with_attr_for_all(VC_ROLE.value(combobox_role));
            return true;
        }
        if (parent->bc_selected_crumb) {
            auto& selected_crumb_ref
                = parent->bc_focused_crumbs[parent->bc_selected_crumb.value()];

            if (!selected_crumb_ref.c_search_placeholder.empty()) {
                value_out = selected_crumb_ref.c_search_placeholder;
                value_out.with_attr_for_all(
                    VC_ROLE.value(role_t::VCR_INACTIVE_STATUS));
                return true;
            }
        }
    }

    return false;
}
<commit_msg>[build] rewrite itertools use<commit_after>/**
 * Copyright (c) 2022, Timothy Stack
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither the name of Timothy Stack nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "breadcrumb_curses.hh"

#include "base/itertools.hh"
#include "itertools.similar.hh"
#include "log_format_fwd.hh"
#include "logfile.hh"

using namespace lnav::roles::literals;

breadcrumb_curses::breadcrumb_curses()
{
    this->bc_match_search_overlay.sos_parent = this;
    this->bc_match_source.set_reverse_selection(true);
    this->bc_match_view.set_selectable(true);
    this->bc_match_view.set_overlay_source(&this->bc_match_search_overlay);
    this->bc_match_view.set_sub_source(&this->bc_match_source);
    this->bc_match_view.set_height(0_vl);
    this->bc_match_view.set_show_scrollbar(true);
    this->bc_match_view.set_default_role(role_t::VCR_POPUP);
    this->add_child_view(&this->bc_match_view);
}

void
breadcrumb_curses::do_update()
{
    if (!this->bc_line_source) {
        return;
    }

    size_t crumb_index = 0;
    size_t sel_crumb_offset = 0;
    auto width = getmaxx(this->bc_window);
    auto crumbs = this->bc_focused_crumbs.empty() ? this->bc_line_source()
                                                  : this->bc_focused_crumbs;
    attr_line_t crumbs_line;
    for (const auto& crumb : crumbs) {
        auto accum_width
            = utf8_string_length(crumbs_line.get_string()).template unwrap();
        auto elem_width = utf8_string_length(crumb.c_display_value.get_string())
                              .template unwrap();
        auto is_selected = this->bc_selected_crumb
            && (crumb_index == this->bc_selected_crumb.value());

        if (is_selected && ((accum_width + elem_width) > width)) {
            crumbs_line.clear();
            crumbs_line.append("\u22ef\u276d"_breadcrumb);
            accum_width = 2;
        }

        crumbs_line.append(crumb.c_display_value);
        if (is_selected) {
            sel_crumb_offset = accum_width;
            crumbs_line.get_attrs().emplace_back(
                line_range{
                    (int) (crumbs_line.length()
                           - crumb.c_display_value.length()),
                    (int) crumbs_line.length(),
                },
                VC_STYLE.template value(A_REVERSE));
        }
        crumb_index += 1;
        crumbs_line.append("\u276d"_breadcrumb);
    }

    line_range lr{0, width};
    view_curses::mvwattrline(
        this->bc_window, this->bc_y, 0, crumbs_line, lr, role_t::VCR_STATUS);

    if (this->bc_selected_crumb) {
        this->bc_match_view.set_x(sel_crumb_offset);
    }
    view_curses::do_update();
}

void
breadcrumb_curses::reload_data()
{
    if (!this->bc_selected_crumb) {
        return;
    }

    auto& selected_crumb_ref
        = this->bc_focused_crumbs[this->bc_selected_crumb.value()];
    this->bc_possible_values = selected_crumb_ref.c_possibility_provider()
        | lnav::itertools::sort_by(&breadcrumb::possibility::p_key);

    nonstd::optional<size_t> selected_value;
    this->bc_similar_values = this->bc_possible_values
        | lnav::itertools::similar_to(
                                  [](const auto& elem) { return elem.p_key; },
                                  this->bc_current_search,
                                  INT_MAX);
    if (selected_crumb_ref.c_key.is<std::string>()) {
        auto& selected_crumb_key = selected_crumb_ref.c_key.get<std::string>();
        auto found_poss_opt = this->bc_similar_values
            | lnav::itertools::find_if([&selected_crumb_key](const auto& elem) {
                                  return elem.p_key == selected_crumb_key;
                              });

        if (found_poss_opt) {
            selected_value = std::distance(this->bc_similar_values.begin(),
                                           found_poss_opt.value());
        } else {
            selected_value = 0;
        }
    }

    auto matches = attr_line_t().join(
        this->bc_similar_values
            | lnav::itertools::map(&breadcrumb::possibility::p_display_value),
        "\n");
    this->bc_match_source.replace_with(matches);
    auto width = this->bc_possible_values
        | lnav::itertools::fold(
                     [](const auto& match, auto& accum) {
                         auto mlen = match.p_display_value.length();
                         if (mlen > accum) {
                             return mlen;
                         }
                         return accum;
                     },
                     selected_crumb_ref.c_display_value.length());

    if (selected_crumb_ref.c_search_placeholder.size() > width) {
        width = selected_crumb_ref.c_search_placeholder.size();
    }
    this->bc_match_view.set_height(vis_line_t(
        std::min(this->bc_match_source.get_lines().size() + 1, size_t{4})));
    this->bc_match_view.set_width(width + 3);
    this->bc_match_view.set_needs_update();
    if (selected_value) {
        this->bc_match_view.set_selection(vis_line_t(selected_value.value()));
        this->bc_match_view.scroll_selection_into_view();
    }
    this->bc_match_view.reload_data();
    this->set_needs_update();
}

void
breadcrumb_curses::focus()
{
    this->bc_focused_crumbs = this->bc_line_source();
    if (this->bc_focused_crumbs.empty()) {
        return;
    }

    this->bc_current_search.clear();
    this->bc_selected_crumb = this->bc_last_selected_crumb.value_or(0);
    this->reload_data();
}

void
breadcrumb_curses::blur()
{
    this->bc_last_selected_crumb = this->bc_selected_crumb;
    this->bc_focused_crumbs.clear();
    this->bc_selected_crumb = nonstd::nullopt;
    this->bc_current_search.clear();
    this->bc_match_view.set_height(0_vl);
    this->bc_match_source.clear();
    this->set_needs_update();
}

bool
breadcrumb_curses::handle_key(int ch)
{
    bool retval = false;

    switch (ch) {
        case KEY_BTAB:
        case KEY_LEFT:
            if (this->bc_selected_crumb) {
                if (this->bc_selected_crumb.value() > 0) {
                    this->bc_selected_crumb
                        = this->bc_selected_crumb.value() - 1;
                } else {
                    this->bc_selected_crumb
                        = this->bc_focused_crumbs.size() - 1;
                }
                this->bc_current_search.clear();
                this->reload_data();
            }
            retval = true;
            break;
        case '\t':
        case KEY_RIGHT:
            if (this->bc_selected_crumb) {
                if (this->bc_selected_crumb.value()
                    < this->bc_focused_crumbs.size() - 1) {
                    this->bc_selected_crumb
                        = this->bc_selected_crumb.value() + 1;
                } else {
                    this->bc_selected_crumb = 0;
                }
                this->bc_current_search.clear();
                this->reload_data();
            }
            retval = true;
            break;
        case KEY_HOME:
            this->bc_match_view.set_selection(0_vl);
            retval = true;
            break;
        case KEY_END:
            this->bc_match_view.set_selection(
                this->bc_match_view.get_inner_height() - 1_vl);
            retval = true;
            break;
        case KEY_NPAGE:
            this->bc_match_view.shift_selection(3);
            retval = true;
            break;
        case KEY_PPAGE:
            this->bc_match_view.shift_selection(-3);
            retval = true;
            break;
        case KEY_UP:
            this->bc_match_view.shift_selection(-1);
            retval = true;
            break;
        case KEY_DOWN:
            this->bc_match_view.shift_selection(1);
            retval = true;
            break;
        case 0x7f:
        case KEY_BACKSPACE:
            if (!this->bc_current_search.empty()) {
                this->bc_current_search.pop_back();
                this->reload_data();
            }
            retval = true;
            break;
        case KEY_ENTER:
        case '\r':
            if (this->bc_selected_crumb) {
                auto& selected_crumb_ref
                    = this->bc_focused_crumbs[this->bc_selected_crumb.value()];
                if (this->bc_match_view.get_selection() >= 0
                    && this->bc_match_view.get_selection()
                        < this->bc_similar_values.size())
                {
                    const auto& new_value
                        = this->bc_similar_values[this->bc_match_view
                                                      .get_selection()]
                              .p_key;

                    selected_crumb_ref.c_performer(new_value);
                } else if (!this->bc_current_search.empty()) {
                    if (selected_crumb_ref.c_possible_range) {
                        size_t index;

                        if (sscanf(
                                this->bc_current_search.c_str(), "%zu", &index)
                            == 1) {
                            selected_crumb_ref.c_performer(index);
                        }
                    } else {
                        selected_crumb_ref.c_performer(this->bc_current_search);
                    }
                }
            }
            break;
        default:
            if (isprint(ch)) {
                this->bc_current_search.push_back(ch);
                this->reload_data();
                retval = true;
            }
            break;
    }

    if (!retval) {
        this->blur();
    }
    this->set_needs_update();
    return retval;
}

bool
breadcrumb_curses::search_overlay_source::list_value_for_overlay(
    const listview_curses& lv,
    int y,
    int bottom,
    vis_line_t line,
    attr_line_t& value_out)
{
    if (y == 0) {
        auto* parent = this->sos_parent;
        auto sel_opt = parent->bc_focused_crumbs
            | lnav::itertools::nth(parent->bc_selected_crumb);
        auto exp_input = sel_opt
            | lnav::itertools::map(&breadcrumb::crumb::c_expected_input)
            | lnav::itertools::unwrap_or(
                             breadcrumb::crumb::expected_input_t::exact);

        value_out.with_attr_for_all(VC_STYLE.value(A_UNDERLINE));

        if (!parent->bc_current_search.empty()) {
            value_out = parent->bc_current_search;

            role_t combobox_role = role_t::VCR_STATUS;
            switch (exp_input) {
                case breadcrumb::crumb::expected_input_t::exact:
                    if (parent->bc_similar_values.empty()) {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                case breadcrumb::crumb::expected_input_t::index: {
                    size_t index;

                    if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
                            != 1
                        || index < 0
                        || (index
                            >= (sel_opt
                                | lnav::itertools::map([](const auto& cr) {
                                      return cr->c_possible_range.value_or(0);
                                  })
                                | lnav::itertools::unwrap_or(size_t{0}))))
                    {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                }
                case breadcrumb::crumb::expected_input_t::index_or_exact: {
                    size_t index;

                    if (sscanf(parent->bc_current_search.c_str(), "%zu", &index)
                        == 1) {
                        if (index < 0
                            || (index
                                >= (sel_opt
                                    | lnav::itertools::map([](const auto& cr) {
                                          return cr->c_possible_range.value_or(
                                              0);
                                      })
                                    | lnav::itertools::unwrap_or(size_t{0}))))
                        {
                            combobox_role = role_t::VCR_ALERT_STATUS;
                        }
                    } else if (parent->bc_similar_values.empty()) {
                        combobox_role = role_t::VCR_ALERT_STATUS;
                    }
                    break;
                }
                case breadcrumb::crumb::expected_input_t::anything:
                    break;
            }
            value_out.with_attr_for_all(VC_ROLE.value(combobox_role));
            return true;
        }
        if (parent->bc_selected_crumb) {
            auto& selected_crumb_ref
                = parent->bc_focused_crumbs[parent->bc_selected_crumb.value()];

            if (!selected_crumb_ref.c_search_placeholder.empty()) {
                value_out = selected_crumb_ref.c_search_placeholder;
                value_out.with_attr_for_all(
                    VC_ROLE.value(role_t::VCR_INACTIVE_STATUS));
                return true;
            }
        }
    }

    return false;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/version.hpp>

#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif

#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else if (addr.to_v6().is_v4_mapped())
			return (addr.to_v6().to_v4() == address_v4::any());
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		error_code ec;
		std::vector<ip_interface> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = i->interface_address;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	// count the length of the common bit prefix
	int common_bits(unsigned char const* b1
		, unsigned char const* b2, int n)
	{
		for (int i = 0; i < n; ++i, ++b1, ++b2)
		{
			unsigned char a = *b1 ^ *b2;
			if (a == 0) continue;
			int ret = i * 8 + 8;
			for (; a > 0; a >>= 1) --ret;
			return ret;
		}
		return n * 8;
	}

	// returns the number of bits in that differ from the right
	// between the addresses.
	int cidr_distance(address const& a1, address const& a2)
	{
		if (a1.is_v4() == a2.is_v4())
		{
			// both are v4
			address_v4::bytes_type b1 = a1.to_v4().to_bytes();
			address_v4::bytes_type b2 = a2.to_v4().to_bytes();
			return address_v4::bytes_type::static_size * 8
				- common_bits(b1.c_array(), b2.c_array(), b1.size());
		}

		address_v6::bytes_type b1;
		address_v6::bytes_type b2;
		if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes();
		else b1 = a1.to_v6().to_bytes();
		if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes();
		else b2 = a2.to_v6().to_bytes();
		return address_v6::bytes_type::static_size * 8
			- common_bits(b1.c_array(), b2.c_array(), b1.size());
	}

	broadcast_socket::broadcast_socket(io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		error_code ec;
		std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);

		if (multicast_endpoint.address().is_v4())
			open_multicast_socket(ios, address_v4::any(), loopback);
		else
			open_multicast_socket(ios, address_v6::any(), loopback);

		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(i->interface_address)) continue;
			// only multicast on compatible networks
			if (i->interface_address.is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(i->interface_address)) continue;

#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
			open_unicast_socket(ios, i->interface_address);
		}
	}

	void broadcast_socket::open_multicast_socket(io_service& ios
		, address const& addr, bool loopback)
	{
		using namespace asio::ip::multicast;

		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		if (addr.is_v4())
			s->open(udp::v4(), ec);
		else
			s->open(udp::v6(), ec);
		if (ec) return;
		s->set_option(datagram_socket::reuse_address(true), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
		if (ec) return;
		s->set_option(join_group(m_multicast_endpoint.address()), ec);
		if (ec) return;
		s->set_option(hops(255), ec);
		if (ec) return;
		s->set_option(enable_loopback(loopback), ec);
		if (ec) return;
		m_sockets.push_back(socket_entry(s));
		socket_entry& se = m_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		using namespace asio::ip::multicast;
		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		if (addr.is_v4())
			s->set_option(outbound_interface(addr.to_v4()), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<commit_msg>fixed bug in broadcast socket (made local service discovery not work)<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/version.hpp>

#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif

#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else if (addr.to_v6().is_v4_mapped())
			return (addr.to_v6().to_v4() == address_v4::any());
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		error_code ec;
		std::vector<ip_interface> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = i->interface_address;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	// count the length of the common bit prefix
	int common_bits(unsigned char const* b1
		, unsigned char const* b2, int n)
	{
		for (int i = 0; i < n; ++i, ++b1, ++b2)
		{
			unsigned char a = *b1 ^ *b2;
			if (a == 0) continue;
			int ret = i * 8 + 8;
			for (; a > 0; a >>= 1) --ret;
			return ret;
		}
		return n * 8;
	}

	// returns the number of bits in that differ from the right
	// between the addresses.
	int cidr_distance(address const& a1, address const& a2)
	{
		if (a1.is_v4() == a2.is_v4())
		{
			// both are v4
			address_v4::bytes_type b1 = a1.to_v4().to_bytes();
			address_v4::bytes_type b2 = a2.to_v4().to_bytes();
			return address_v4::bytes_type::static_size * 8
				- common_bits(b1.c_array(), b2.c_array(), b1.size());
		}

		address_v6::bytes_type b1;
		address_v6::bytes_type b2;
		if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes();
		else b1 = a1.to_v6().to_bytes();
		if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes();
		else b2 = a2.to_v6().to_bytes();
		return address_v6::bytes_type::static_size * 8
			- common_bits(b1.c_array(), b2.c_array(), b1.size());
	}

	broadcast_socket::broadcast_socket(io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		error_code ec;
		std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);

		if (multicast_endpoint.address().is_v4())
			open_multicast_socket(ios, address_v4::any(), loopback);
		else
			open_multicast_socket(ios, address_v6::any(), loopback);

		for (std::vector<ip_interface>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (is_local(i->interface_address)) continue;
			// only multicast on compatible networks
			if (i->interface_address.is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(i->interface_address)) continue;

#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
			open_unicast_socket(ios, i->interface_address);
		}
	}

	void broadcast_socket::open_multicast_socket(io_service& ios
		, address const& addr, bool loopback)
	{
		using namespace asio::ip::multicast;

		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		if (addr.is_v4())
			s->open(udp::v4(), ec);
		else
			s->open(udp::v6(), ec);
		if (ec) return;
		s->set_option(datagram_socket::reuse_address(true), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
		if (ec) return;
		s->set_option(join_group(m_multicast_endpoint.address()), ec);
		if (ec) return;
		s->set_option(hops(255), ec);
		if (ec) return;
		s->set_option(enable_loopback(loopback), ec);
		if (ec) return;
		m_sockets.push_back(socket_entry(s));
		socket_entry& se = m_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		using namespace asio::ip::multicast;
		error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		if (addr.is_v4())
			s->set_option(outbound_interface(addr.to_v4()), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<|endoftext|>