### BUGGY CODE ###

import queue
h, w = map(int, input().split())
bigger = h if h > w else w
dfs = [[0]*bigger]*bigger
q = queue.Queue
d = []
for i in range(h):
  s = [j for j in input()]
  d.append(s)
from collections import defaultdict
def find_possible(i, j, ng):
  if i >= h-1:
    iaround = [i, i-1]
  elif i<=0:
    iaround = [i, i+1]
  else:
    iaround = [i, i-1, i+1]
  if j >= w-1:
    jaround = [j, j-1]
  elif j<=0:
    jaround = [j, j+1]
  else:
    jaround = [j, j-1, j+1]
  retv = []
  for a1 in iaround:
    for a2 in jaround:
      if [a1, a2] in ng or abs(a1-i) == abs(a2-j):
        continue
      elif d[a1][a2] == '.':
        retv.append([a1, a2])
  #print(i,j,retv)
  return retv

def bfs(qu):
  #includes points=[y][x], distance, parents=[[y][x]]
  used = [[-1 for _ in range(w)] for _ in range(h)]
  m = 0
  while not qu.empty():
    current = qu.get()
    row = current[0][0]
    column = current[0][1]
#    print(row, column, used[row][column])
    if used[row][column] > current[1] or used[row][column]<0:
      used[row][column] = current[1]
#      print(used)
#      print('update:', current[1])
      for a in find_possible(row, column, current[2]):
#          print('extend:',a,current[1]+1, tmp)          
        qu.put([a,current[1]+1, current[2] + [[row, column]]])
   #       if current[1]+1 > m:
   #         m = current[1]+1
#    print()
  m = 0
  return used
m = 0
cans = []
for i in range(h):
  for j in range(w):
    if d[i][j] == '#':
      continue
    q = queue.Queue()
    q.put([[i,j], 0, []])
    t = bfs(q)
    cans.append(t)
import numpy as np
m = np.array(cans)

print(np.max(m))
    

### FIXED CODE ###

import queue
h,w=map(int,input().split())
s=[list(input()) for i in range(h)]

inf=100000000
ma=[]
for i in range(h):
    for j in range(w):
        if s[i][j]==".":
            x=[[inf for i_sub in range(w)] for j_sub in range(h)]
            #いまみているグリッド
            q=queue.Queue()
            d=0
            #初めのグリッド
            q.put([i,j])
            x[i][j]=0
            #次々にグリッドを辿って全部のグリッド辿ったら終わり
            while q.qsize()!=0:
                #距離を加算
                d+=1
                l=q.qsize()
                #今いるグリッドの数は数えておく
                for t in range(l):
                    I,J=q.get()
                    #今いるグリッドから次のグリッドへ
                    #その判定は"そこにグリッドがあるか","そこが辿れるのか","すでに辿っているのか"の三つです。
                    if I-1>=0 and s[I-1][J]=="." and x[I-1][J]==inf:
                        q.put([I-1,J])
                        #この更新をif文内で行わないと余計にグリッドを調べてしまう
                        x[I-1][J]=d
                    if I+1<=h-1 and s[I+1][J]=="." and x[I+1][J]==inf:
                        q.put([I+1,J])
                        x[I+1][J]=d
                    if J-1>=0 and s[I][J-1]=="." and x[I][J-1]==inf:
                        q.put([I,J-1])
                        x[I][J-1]=d
                    if J+1<=w-1 and s[I][J+1]=="." and x[I][J+1]==inf:
                        q.put([I,J+1])
                        x[I][J+1]=d
            ma_sub=0
            for k in range(h):
                for l in range(w):
                    if x[k][l]!=inf:
                        ma_sub=max(ma_sub,x[k][l])
            ma.append(ma_sub)

print(max(ma))