subdf = function(x, i, j, drop = TRUE) {

    # preliminary checks etc.
    if (!is.data.frame(x)) stop("the argument 'x' must be a data frame")
    has.i = !missing(i)
    has.j = !missing(j)
    has.drop = !missing(drop)
    nij = nargs() - has.drop - 1L
    if (has.i && any(is.na(i))) stop(sprintf('undefined %s selected', if (nij == 2L) 'rows' else 'columns'))
    if (has.j && any(is.na(j))) stop('undefined columns selected')
        
    # both i and j specified: select rows by i and columns by j, optional dropping
    if (has.i && has.j) {
        rownames = .row_names_info(x, 0L)
        nrows = length(rownames)
        if (is.logical(i)) {
            if (is.na(max(i))) stop('undefined rows selected')
            else i = seq(nrows)[i] }
        else if (is.character(i)) i = pmatch(i, rownames, duplicates.ok=TRUE)
        maxi = max(i)
        if (is.na(maxi) || maxi > nrows) stop('undefined rows selected')
        colnames = attr(x, 'names')
        ncols = length(colnames)
        if(is.logical(j)) j = seq(ncols)[j]
        else if (is.character(j)) j = pmatch(j, colnames, duplicates.ok=TRUE)
        maxj = max(j)
        if (is.na(maxj) || maxj > ncols) stop('undefined columns selected')
        if (drop) {
            if (length(j) == 1L) return (.subset2(x, j)[i])
            if (has.drop && length(i) == 1L) return (lapply(.subset(x, j), '[', i)) }
        return (structure(lapply(.subset(x, j), '[', i), 
            row.names={
                if (any(duplicated(i))) make.unique(as.character(rownames[i]))
                else rownames[i] },
            names={
                if (any(duplicated(j))) make.unique(colnames[j])
                else colnames[j] },
            class='data.frame')) }

    # two-index form, only i specified: select rows by i, keep all columns, optional dropping
    if (has.i && nij == 2L) { 
        rownames = .row_names_info(x, 0L)
        nrows = length(rownames)
        if (is.logical(i)) {
            if (is.na(max(i))) stop('undefined rows selected')
            else i = seq(nrows)[i] }
        else if (is.character(i)) i = pmatch(i, rownames, duplicates.ok=TRUE)
        maxi = max(i)
        if (is.na(maxi) || maxi > nrows) stop('undefined rows selected')
        if (drop) {
            if (length(x) == 1L) return (.subset2(x, 1)[i])
            if (has.drop && length(i) == 1L) return (lapply(x, '[', i)) }
        return (structure(lapply(x, '[', i), 
            row.names={
                if (any(duplicated(i))) make.unique(as.character(rownames[i]))
                else rownames[i] },
            names=attr(x, 'names'), 
            class='data.frame')) }

    # one-index form, only i specified: select columns by i, no dropping
    if (has.i) { 
        colnames = attr(x, 'names')
        ncols = length(colnames)
        if(is.logical(i)) i = seq(ncols)[i]
        else if (is.character(i)) i = pmatch(i, colnames, duplicates.ok=TRUE)
        maxi = max(i)
        if (is.na(maxi) || maxi > ncols) stop('undefined columns selected')
        if (has.drop && drop) warning('drop argument will be ignored')
        return (structure(.subset(x, i),
            row.names=attr(x, 'row.names'), 
            names={
                if (any(duplicated(i))) make.unique(colnames[i])
                else colnames[i] },
            class='data.frame')) }

    # one- or two-index form, only j specified: select columns by j, optional droping
    if (has.j) {
        colnames = attr(x, 'names')
        ncols = length(colnames)
        if(is.logical(j)) j = seq(ncols)[j]
        else if (is.character(j)) j = pmatch(j, colnames, duplicates.ok=TRUE)
        maxj = max(j)
        if (is.na(maxj) || maxj > ncols) stop('undefined columns selected')
        rownames = attr(x, 'row.names')
        if (drop) {
            if (length(j) == 1L) return (.subset2(x, j))
            if (has.drop && length(rownames) == 1L) return (.subset(x, j)) }
        return (structure(.subset(x, j),
            row.names=rownames, 
            names={
                if (any(duplicated(j))) make.unique(colnames[j])
                else colnames[j] },
            class='data.frame')) }

    # neither i nor j specified: keep all columns and rows, optional or no dropping
    if (drop && has.drop) {
        if (length(attr(x, 'row.names')) == 1L) return (as.list(x))
        if (length(x) == 1L) warning('drop argument will be ignored') }

    return (x) }