"?" <- function(e1, e2)
{
    e1Expr <- substitute(e1)
    if(missing(e2)) {
        if(is.call(e1Expr))
            return(.helpForCall(e1Expr, parent.frame()))
        if(is.name(e1Expr))
            e1 <- as.character(e1Expr)
        eval(substitute(help(TOPIC), list(TOPIC = e1)))
    }
  else {
      ## interpret e1 as a type, but to allow customization, do NOT force
      ## arbitrary expressions to be single character strings (so that methods
      ## can be defined for topicName).
        if(is.name(e1Expr))
            e1 <- as.character(e1Expr)
      e2Expr <- substitute(e2)
    if(is.name(e2Expr))
      e2 <- as.character(e2Expr)
        else if(is.call(e2Expr) && identical(e1, "method"))
            return(.helpForCall(e2Expr, parent.frame(), FALSE))
    topic <- topicName(e1, e2)
    doHelp <- .tryHelp(topic)
    if(inherits(doHelp, "try-error")) {
        stop(paste("no documentation of type \"", e1,
                   "\" and topic \"", e2,
                   "\" (or error in processing help)", sep=""))
    }
  }
}

topicName <- function(type, topic) {
    if((length(type) == 0) || (length(topic) == 0))
        character(0)
    else
        paste(paste(topic, collapse = ","), type, sep = "-")
}

.helpForCall <- function(expr, envir, doEval = TRUE) {
    f <- expr[[1]] # the function specifier
    where <- topenv(envir) # typically .GlobalEnv
    if(is.name(f))
        f <- as.character(f)
    if(!.isMethodsDispatchOn() || !isGeneric(f, where = where)){
        if(!is.character(f) || length(f) != 1)
            stop("The object of class \"", class(f), "\" in the function call \"",
                 deparse(expr), "\"could not be used as a documentation topic")
        h <- .tryHelp(f)
        if(inherits(h, "try-error"))
            stop("No methods for \"", f, "\" and no documentation for it as a function")
    }
    else {
        ## allow generic function objects or names
        if(is(f, "genericFunction")) {
            fdef <- f
            f <- fdef@generic
        }
        else
            fdef <- getGeneric(f, where = where)
        call <- match.call(fdef, expr)
        ## make the signature
        sigNames <- fdef@signature
        sigClasses <- rep.int("missing", length(sigNames))
        names(sigClasses) <- sigNames
        for(arg in sigNames) {
            argExpr <- elNamed(call, arg)
            if(!is.null(argExpr)) {
                simple <- (is.character(argExpr) || is.name(argExpr))
                ## TODO:  ideally, if doEval is TRUE, we would like to create
                ## the same context used by applyClosure in eval.c, but then
                ## skip the actual evaluation of the body.  If we could create
                ## this environment then passing it to selectMethod is closer to
                ## the semantics of the "real" function call than the code below.
                ## But, seems to need a change to eval.c and a flag to the evaluator.
                if(doEval || !simple) {
                    argVal <- try(eval(argExpr, envir))
                    if(is(argVal, "try-error"))
                        stop("Error in trying to evaluate the expression for argument \"",
                             arg, "\" (", deparse(argExpr), ")")
                    elNamed(sigClasses, arg) <- class(argVal)
                }
                else
                    elNamed(sigClasses, arg) <- as.character(argExpr)
            }
        }
        method <- selectMethod(f, sigClasses, optional=TRUE, fdef = fdef)
        if(is(method, "MethodDefinition"))
            sigClasses <- method@defined
        else
            warning("No method defined for function \"", f,
                    "\" and signature ",
                    paste(sigNames, " = \"", sigClasses, "\"", sep = "", collapse = ", "))
        topic <- topicName("method", c(f,sigClasses))
        h <- .tryHelp(topic)
        if(is(h, "try-error"))
            stop("No documentation for function \"", f,
                 "\" and signature ",
                 paste(sigNames, " = \"", sigClasses, "\"", sep = "", collapse = ", "))
    }
}

.tryHelp <- function(topic) {
    opts <- options(error= function()TRUE, show.error.messages = FALSE)
    on.exit(options(opts))
    try(do.call("help", list(topic)))
}

