using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    public class IPoint
    {
        public int X { get; private set; } = 0;
        public int Y { get; private set; } = 0;
        public IPoint(int x, int y)
        {
            X = x;
            Y = y;
        }

        public override bool Equals(object obj)
        {
            if(obj == null || !(obj is IPoint))
            {
                return false;
            }
            else
            {
                var tmp = obj as IPoint;
                return (this.X == tmp.X && this.Y == tmp.Y);
            }
        }
    }

    static void Main(string[] args)
    {
        var input = Console.ReadLine().Split().Select(x => int.Parse(x)).ToArray();
        var _H = input[0];
        var _W = input[1];
        var maze = new bool[_H, _W];
        for(var i = 0; i < _H; i++)
        {
            var line = Console.ReadLine();
            for(var j = 0; j < _W; j++)
            {
                maze[i, j] = (line[j] == '.');
            }
        }

        int max = 0;
        for(var i1 = 0; i1 < _H; i1++)
        {
            for(var j1 = 0; j1 < _W; j1++)
            {
                for (var i2 = 0; i2 < _H; i2++)
                {
                    for (var j2 = 0; j2 < _W; j2++)
                    {
                        var tmp = ClearMaze(maze, new IPoint(i1, j1), new IPoint(i2, j2));
                        if(max < tmp)
                        {
                            max = tmp;
                        }
                    }
                }
            }
        }
        Console.WriteLine(max);

#if DEBUG
        Console.ReadKey();
#endif
    }

    static int ClearMaze(bool[,] maze, IPoint start, IPoint end)
    {
        if (maze[start.X, start.Y] && maze[end.X, end.Y] && !start.Equals(end))
        {
            var _H = maze.GetLength(0);
            var _W = maze.GetLength(1);
            var score = new long[_H, _W];
            for(var i = 0; i < _H; i++)
            {
                for (var j = 0; j < _W; j++)
                {
                    score[i, j] = int.MaxValue;
                }
            }
            var nextStartPoint = new List<IPoint>();
            nextStartPoint.Add(start);
            for (var i = 1; i <= int.MaxValue; i++)
            {
                var tmpNextStartPoint = new List<IPoint>();
                foreach(var point in nextStartPoint)
                {
                    var tmpArray = new List<IPoint>() { new IPoint(point.X - 1, point.Y),
                                                        new IPoint(point.X + 1, point.Y), 
                                                        new IPoint(point.X,     point.Y - 1), 
                                                        new IPoint(point.X,     point.Y + 1) };
                    foreach(var tmp in tmpArray)
                    {
                        if (tmp.X >= 0 && tmp.Y>= 0 && tmp.X < _H && tmp.Y < _W && maze[tmp.X, tmp.Y] && score[tmp.X, tmp.Y] > i)
                        {
                            score[tmp.X, tmp.Y] = i;
                            tmpNextStartPoint.Add(tmp);
                            if (tmp.Equals(end))
                            {
                                return i;
                            }
                        }
                    }
                }
                nextStartPoint = tmpNextStartPoint;
            }
        }
        return -1;
    }
}
