package main

import "fmt"

type point struct {
	name int
	pos_h int
	pos_w int
	right int
	left int
	up int
	down int
	deep int
	checked bool
}

func pop(p []point) (point, []point) {
	//fmt.Println("pop! name is ", p[0].name)
	p[0].checked = true
	pp := p[0]
	p = p[1:]
	return pp, p
}

func push(data point, p []point) []point {
	//fmt.Println("push! name is ", data.name)
	data.checked = true
	p = append(p, data)
	//fmt.Println(p)
	return p
}

func dataChecked(p point, data []point) []point {
	for i := 0 ; i < len(data) ; i++ {
		if p.name == data[i].name {
			data[i].checked = true
			break
		}
	}
	return data
}

func reset(p []point) []point {
	for i := 0 ; i < len(p) ; i++ {
		p[i].deep = 0
		p[i].checked = false
	}
	return p
}
/*
func printQueue(p []point){
	queue := make([]int, len(p))
	for i := 0; i < len(p); i++ {
		fmt.Println("Name : ",p[i].name)
		fmt.Println("Height : ",p[i].pos_h)
		fmt.Println("Width : ",p[i].pos_w)
		fmt.Println("Right : ",p[i].right)
		fmt.Println("Left : ",p[i].left)
		fmt.Println("Up : ",p[i].up)
		fmt.Println("Down : ",p[i].down)
		fmt.Println("Deep : ",p[i].deep)
		fmt.Println("")
		queue[i] = p[i].name
	}
	fmt.Println(queue)
}*/

func address(p []point, h int, w int) point {
	var re point
	for i := 0 ; i < len(p) ; i++ {
		if p[i].pos_h == h && p[i].pos_w == w {
			re = p[i]
		}
	}
	return re
}

func searchName(n int, p []point) point {
	var c point
	for i := 0 ; i < len(p) ; i++ {
		if p[i].name == n {
			c = p[i]
			break
		}
	}
	return c
}


func main(){
	//var queue []point
	var h,w int
	fmt.Scan(&h,&w)
	route := make([][]bool, h)
	for i := 0 ; i < h ; i++ {
		route[i] = make([]bool, w)
	}
	var d string
	//fmt.Println(route)
	count := 0
	for i := 0 ; i < h ; i++ {
		fmt.Scan(&d)
		for j := 0 ; j < w ; j++ {
			if d[j:j+1] == "." {
				route[i][j] = true
				count++
			}
		}
	}
	//fmt.Println(route)
	data := make([]point,count)
	count = 0
	for i := 0 ; i < h ; i++ {
		for j := 0 ; j < w ; j++ {
			if route[i][j] {
				data[count].name = count+1
				data[count].pos_h = i
				data[count].pos_w = j
				count++
			}
		}
	}
	for i := 0 ; i < count ; i++{
		if data[i].pos_h != 0 && route[data[i].pos_h-1][data[i].pos_w] {
			temp := address(data,data[i].pos_h-1,data[i].pos_w)
			data[i].up = temp.name
		}
		if data[i].pos_h != h-1 && route[data[i].pos_h+1][data[i].pos_w] {
			temp := address(data,data[i].pos_h+1,data[i].pos_w)
			data[i].down = temp.name
		}
		if data[i].pos_w != 0 && route[data[i].pos_h][data[i].pos_w-1] {
			temp := address(data,data[i].pos_h,data[i].pos_w-1)
			data[i].left = temp.name
		}
		if data[i].pos_w != w-1 && route[data[i].pos_h][data[i].pos_w+1] {
			temp := address(data,data[i].pos_h,data[i].pos_w+1)
			data[i].right = temp.name
		}
	}
	//printQueue(data)
	max := 0
	var root,leaf point
	for i := 0 ; i < count-1 ; i++ {
		for j := i+1 ; j < count ; j++ {
			//fmt.Println("Search now : ",i+1,j+1)
			data = reset(data)
			var queue []point
			//data[i] -> data[j] distance
			//BFS
			queue = push(data[i] , queue)
			data[i].checked = true
			for ;; {
				//printQueue(queue)
				//fmt.Println("Search now : ",i,j)
				if len(queue) == 0{
					//fmt.Println("deadrock")
					break
				}
				root,queue = pop(queue)
				//fmt.Println("root name is ", root.name)
				if root.name == data[j].name {
					//fmt.Println("Distance is ",root.deep)
					if max < root.deep {
						max = root.deep
					}
					break
				}
				if root.up != 0 {
					//checked?
					leaf = searchName(root.up,data)
					data = dataChecked(leaf,data)
					//fmt.Println("Up:",leaf)
					if !leaf.checked {
						leaf.deep = root.deep + 1
						queue = push(leaf , queue)
					}
				}
				if root.left != 0 {
					leaf = searchName(root.left,data)
					data = dataChecked(leaf,data)
					//fmt.Println("Left:",leaf)
					if !leaf.checked {
						leaf.deep = root.deep + 1
						queue = push(leaf , queue)
					}
				}
				if root.down != 0 {
					leaf = searchName(root.down,data)
					data = dataChecked(leaf,data)
					//fmt.Println("Down:",leaf)
					if !leaf.checked {
						leaf.deep = root.deep + 1
						queue = push(leaf , queue)
					}
				}
				if root.right != 0{
					leaf = searchName(root.right,data)
					data = dataChecked(leaf,data)
					//fmt.Println("Right:",leaf)
					if !leaf.checked {
						leaf.deep = root.deep + 1
						queue = push(leaf , queue)
					}
				}
			}
		}
	}
	fmt.Println(max)
}
