using System;
using System.Collections.Generic;
using System.Linq;
 
public class AchieveTheGoal {
    public enum Direction : int
    {
        Right = 0,
        Left,
        Up,
        Down
    };
    public static int H, W;
    
    public static void Main(){
        string[] line = Console.ReadLine().Split(' ');
        H = int.Parse(line[0]);
        W = int.Parse(line[1]);
        char[,] map = new char[H, W];

        // Mapの構築
        for (int i = 0; i < H ; i++ )
        {
            string newline = Console.ReadLine();
            for (int j = 0; j < W ; j++ )
            {
                map[i, j] = newline.ToCharArray()[j];
            }
        }
        
        int maxCount = 0;
        // 全通り探索する
        // 探索方法は、幅優先探索
        for (int i = 0; i < H ; i++ )
        {
            for (int j = 0; j < H ; j++ )
            {
                //Console.WriteLine(i +", "+j+":");
                int treeDepth = GetMaxDepth(map, new Point(i,j));
                if (maxCount < treeDepth)
                {
                    maxCount = treeDepth;
                }
            }
        }
        
        Console.WriteLine(maxCount);
    }
    
    private static int GetMaxDepth(char[,] map, Point startPoint)
    {
        // スタート地点が壁の場合
        if (map[startPoint.y, startPoint.x] == '#')
        {
            // 長さを0で返す
            return 0;
        }
        
        int maxTreeDepth = 0;
        startPoint.treeDepth = 0;
        Queue<Point> queue = new Queue<Point>();
        queue.Enqueue(startPoint);
        // startPointは既訪問
        Dictionary<string, bool> hasAlreadyVisited = new Dictionary<string, bool>();
        string pointID = startPoint.x.ToString() + startPoint.y.ToString();
        
        while (queue.Count > 0)
        {
            int nextSearchCount = queue.Count;
            for (int i = 0; i < nextSearchCount ; i++)
            {
                Point next = queue.Dequeue();
                pointID = next.x.ToString() + next.y.ToString();
                //Console.WriteLine(next.x +" "+next.y);
                // 未訪問の場合のみ続行
                if (!hasAlreadyVisited.ContainsKey(pointID))
                {
                    hasAlreadyVisited[pointID] = true;
                    VisitNextPoint(map, next, queue);
                    maxTreeDepth = next.treeDepth;
                    //Console.WriteLine(maxTreeDepth);
                }
            }
        }
        return maxTreeDepth;
    }
    
    private static void VisitNextPoint(char[,] map, Point p, Queue<Point> queue)
    {
        if (MoveCheck(map, p, (int)Direction.Right)) 
        {
            Point rightPoint = new Point(p.x+1, p.y);
            rightPoint.treeDepth = p.treeDepth + 1;
            queue.Enqueue(rightPoint);
        }
        if (MoveCheck(map, p, (int)Direction.Left))
        {
            Point leftPoint = new Point(p.x-1, p.y);
            leftPoint.treeDepth = p.treeDepth + 1;
            queue.Enqueue(leftPoint);
        }
        if (MoveCheck(map, p, (int)Direction.Up))
        {
            Point upPoint = new Point(p.x, p.y-1);
            upPoint.treeDepth = p.treeDepth + 1;
            queue.Enqueue(upPoint);
        }
        if (MoveCheck(map, p, (int)Direction.Down))
        {
            Point downPoint = new Point(p.x, p.y+1);
            downPoint.treeDepth = p.treeDepth + 1;
            queue.Enqueue(downPoint);
        }
    }
    
    private static bool MoveCheck(char[,] map, Point p, int dir)
    {
        if (dir == (int)Direction.Right)
        {
            if (p.x+1 >= W) 
            {
                return false;
            }
            else
            {
                if (map[p.y, p.x+1] == '.')
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        else if (dir == (int)Direction.Left)
        {
            if (p.x-1 < 0) 
            {
                return false;
            }
            else
            {
                if (map[p.y, p.x-1] == '.')
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        else if (dir == (int)Direction.Up)
        {
            if (p.y-1 < 0) 
            {
                return false;
            }
            else
            {
                if (map[p.y-1, p.x] == '.')
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        else if (dir == (int)Direction.Down)
        {
            if (p.y+1 >= H) 
            {
                return false;
            }
            else
            {
                if (map[p.y+1, p.x] == '.')
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        return false;
    }
}

public class Point {
    public int x, y;
    public int treeDepth;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

}