
import java.util.*
import java.lang.*

fun main(args: Array<String>) {

    val (H ,M) = readLine()!!.split(" ").map(String::toInt)

    //map作成
    //true = road,false = wall
    var map = LinkedHashMap<Int,LinkedHashMap<Int,Boolean>>()
    for(i in 0 until H){
        map[i] = LinkedHashMap()
        var yoko = readLine()!!.toCharArray().map(Char::toString)
        for(j in 0 until M){
            map[i]!![j] = yoko[j]=="."
        }
    }

    var answer = 0
    for(i in 0 until H){
        for(j in 0 until M){
            if(map[i]!![j]!!){
                var max = bnf(map,i,j)//探索
                answer = Math.max(max,answer)
            }
        }
    }

    println(answer)

}

fun bnf(map : LinkedHashMap<Int,LinkedHashMap<Int,Boolean>>,Y : Int,X : Int) : Int{

    var stack = ArrayDeque<Int>()//探索キュー
    var old = ArrayList<Int>()//探索済みList
    var yoko = map[0]!!.size//横幅
    var tate = map.size//縦幅
    var costs = LinkedHashMap<Int,Int>()//コスト

    //方角に動けるか
    var N = false
    var E = false
    var S = false
    var W = false

    //override
    var X = X
    var Y = Y

    var index = X + Y*yoko
    costs[index] = 0//初期化

    while (true){

        //座標
        index = X + Y*yoko

        //コスト
        var cost = costs[index]!!

        //初期化
        N = false
        E = false
        S = false
        W = false

        //方角チェック
        if(X!=0)W=map[Y]!![X-1]!!
        if(X!=yoko-1)E=map[Y]!![X+1]!!
        if(Y!=0)N=map[Y-1]!![X]!!
        if(Y!=tate-1)S=map[Y+1]!![X]!!

        //スタック追加
        var nIndex = index-yoko
        if(N && !stack.contains(nIndex)){
            if(!old.contains(nIndex)){
                stack.offer(nIndex)
                costs[nIndex] = cost+1
            }else{
                costs[nIndex] = Math.min(costs[nIndex]!!,cost+1)
            }
        }
        var eIndex = index+1
        if(E && !stack.contains(eIndex)){
            if(!old.contains(eIndex)){
                stack.offer(eIndex)
                costs[eIndex] = cost+1
            }else{
                costs[eIndex] = Math.min(costs[eIndex]!!,cost+1)
            }
        }
        var sIndex = index+yoko
        if(S && !stack.contains(sIndex)){
            if(!old.contains(sIndex)){
                stack.offer(sIndex)
                costs[sIndex] = cost+1
            }else{
                costs[sIndex] = Math.min(costs[sIndex]!!,cost+1)
            }
        }
        var wIndex = index-1
        if(W && !stack.contains(wIndex)){
            if(!old.contains(wIndex)){
                stack.offer(wIndex)
                costs[wIndex] = cost+1
            }else{
                costs[wIndex] = Math.min(costs[wIndex]!!,cost+1)
            }
        }

        //探索済みに
        old.add(index)

        //進めない&stackなし => break
        if(stack.size == 0){
            break
        }

        //xy更新
        index = stack.poll()
        X = index%yoko
        Y = index/yoko
    }

    return costs.values.max() ?: 0
}