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

namespace Contest
{
    partial class Program
    {
        static void Main(string[] args)
        {
            ABC151D();
        }

        static void ABC151A()
        {
            var s = ReadStr();
            Console.WriteLine((char)(s[0] + 1));
        }
        static void ABC151B()
        {
            var nkm = ReadIntN();
            var A = ReadIntN();
            var N = nkm[0];
            var K = nkm[1];
            var M = nkm[2];
            var goal = N * M - A.Sum();
            Console.WriteLine(goal > K ? -1 : (goal < 0 ? 0 : goal));
        }
        static void ABC151C()
        {
            var nm = ReadIntN();
            var PS = ReadTupleStr(nm[1]);
            var N = nm[0];
            int[] ac = new int[N + 1];
            int[] wa = new int[N + 1];
            foreach(var ps in PS)
            {
                var n = Convert.ToInt32(ps.Item1);
                if (ps.Item2 == "AC")
                {
                    ac[n] = 1;
                }
                else
                {
                    if (ac[n] == 0) { wa[n]++; }
                }
            }
            long countAC = ac.Sum();
            long countWA = Enumerable.Range(0, ac.Length).Select(i => ac[i] > 0 ? wa[i] : 0).Sum();
            Console.WriteLine("{0} {1}", countAC, countWA);
        }
        static void ABC151D()
        {
            var hw = ReadIntN();
            var H = hw[0];
            var W = hw[1];
            bool[,] road = new bool[H + 2, W + 2];
            for (int h = 0; h < H; ++h)
            {
                var str = ReadStr();
                for (int w = 0; w < W; ++w)
                {
                    road[h + 1, w + 1] = str[w] == '.';
                }
            }
            Tuple<int, int>[] moves = new Tuple<int, int>[] { new Tuple<int, int>(-1, 0), new Tuple<int, int>(1, 0), new Tuple<int, int>(0, -1), new Tuple<int, int>(0, 1) };
            int max = 0;
            for(int startH = 1; startH <= H; ++startH)
            {
                for(int startW = 1; startW <= W; ++startW)
                {
                    bool[,] done = new bool[H + 2, W + 2];
                    List<Tuple<int,int>> list = new List<Tuple<int, int>>();
                    done[startH, startW] = true;
                    list.Add(new Tuple<int, int>(startH, startW));
                    int counter = -1;
                    while (list.Count > 0)
                    {
                        List<Tuple<int, int>> next = new List<Tuple<int, int>>();
                        foreach (var t in list)
                        {
                            foreach(var move in moves)
                            {
                                var h = t.Item1 + move.Item1;
                                var w = t.Item2 + move.Item2;
                                if(!done[h,w] && road[h, w])
                                {
                                    done[h, w] = true;
                                    next.Add(new Tuple<int, int>(h, w));
                                }
                            }
                        }
                        list = next;
                        counter++;
                    }
                    max = Math.Max(max, counter);
                }
            }
            Console.WriteLine(max);
        }
        static void ABC151E()
        {
            var nk = ReadIntN();
            var A = ReadLongN();
            var N = nk[0];
            var K = nk[1];
            A = A.OrderBy(x => x).ToArray();
            var combFunc = Util.Combin(N, Util.P);
            long sumMax = 0;
            long sumMin = 0;
            for(int i = 0; i < N; ++i)
            {
                sumMax = (sumMax + combFunc(i, K - 1) * A[i]) % Util.P;
                sumMin = (sumMin + combFunc(N - i - 1, K - 1) * A[i]) % Util.P;
            }
            Console.WriteLine((sumMax - sumMin + Util.P * 10) % Util.P);
        }
        static void ABC151F()
        {
            var N = ReadInt();
            var XY = ReadTupleDouble(N);
            double eps = 0.000001;

            double min = Double.MaxValue;

            /// 2点間の距離
            Func<Tuple<double, double>, Tuple<double, double>, double> distance = (a, b) =>
            {
                var diff1 = a.Item1 - b.Item1;
                var diff2 = a.Item2 - b.Item2;
                return Math.Sqrt(diff1 * diff1 + diff2 * diff2);
            };

            /// 最小の半径を与える円は以下のいずれか
            /// (1) ある2点を直径とする
            /// (2) ある3点を通る

            /// ある2点を直径とする
            for (int i = 0; i < N; ++i)
            {
                for(int j = i + 1; j < N; ++j)
                {
                    var p = XY[i];
                    var q = XY[j];

                    ///中心o 半径r
                    var o = new Tuple<double, double>((p.Item1 + q.Item1) / 2, (p.Item2 + q.Item2) / 2);
                    var r = distance(p, o);

                    /// 全点がoからr以内にあればOK
                    bool ok = true;
                    for(int k = 0; k < N; ++k)
                    {
                        if(distance(XY[k], o) > r + eps)
                        {
                            ok = false;
                            break;
                        }
                    }
                    if (ok)
                    {
                        min = Math.Min(min, r);
                    }
                }
            }

            /// ある3点を通る
            for (int i = 0; i < N; ++i)
            {
                var I = XY[i];
                for (int j = i + 1; j < N; ++j)
                {
                    var J = XY[j];
                    /// 線分(I,J)の垂直二等分線 a1 x + b1 y + c1 =0
                    var a1 = J.Item1 - I.Item1;
                    var b1 = J.Item2 - I.Item2;
                    var c1 = -a1 * (I.Item1 + J.Item1) / 2 - b1 * (I.Item2 + J.Item2) / 2;
                    for (int k = j + 1; k < N; ++k)
                    {
                        var K = XY[k];
                        /// 線分(I,K)の垂直二等分線 a2 x + b2 y + c2 =0
                        var a2 = K.Item1 - I.Item1;
                        var b2 = K.Item2 - I.Item2;
                        var c2 = -a2 * (I.Item1 + K.Item1) / 2 - b2 * (I.Item2 + K.Item2) / 2;

                        /// 垂直二等分線が平行＝3点が同一直線上にあるので円は存在しない
                        var a1b2a2b1 = a1 * b2 - a2 * b1;
                        if (Math.Abs(a1b2a2b1) < eps)
                        {
                            continue;
                        }

                        /// 垂直二等分線の交点が円の中心
                        var ox = (c2 * b1 - c1 * b2) / a1b2a2b1;
                        var oy = -(c2 * a1 - c1 * a1) / a1b2a2b1;
                        var o = new Tuple<double, double>(ox, oy);
                        var r = distance(o, I);

                        /// 全点がoからr以内にあればOK
                        bool ok = true;
                        for (int l = 0; l < N; ++l)
                        {
                            if (distance(XY[l], o) > r + eps)
                            {
                                ok = false;
                                break;
                            }
                        }
                        if (ok)
                        {
                            min = Math.Min(min, r);
                        }
                    }
                }
            }
            Console.WriteLine(min);
        }


        class Util
        {
            public static readonly long P = 1000000007;

            public static Dictionary<long,int> Factorize(long x)
            {
                var dict = new Dictionary<long, int>();
                long minp = 2;
                while (x > 1)
                {
                    bool ok = false;
                    for(long p = minp; p * p <= x; ++p)
                    {
                        if (x % p == 0)
                        {
                            if (!dict.ContainsKey(p))
                            {
                                dict.Add(p, 0);
                            }
                            dict[p]++;
                            minp = p;
                            x /= p;
                            ok = true;
                            break;
                        }
                    }
                    if (!ok)
                    {
                        if (!dict.ContainsKey(x)) { dict.Add(x, 0); }
                        dict[x]++;
                        break;
                    }
                }
                return dict;
            }
            public static long Inverse(long a, long p)
            {
                long b = p, c = 1, d = 0, e = 0, f = 0;
                while (b != 0)
                {
                    e = a / b;
                    f = b; b = a - e * b; a = f;
                    f = d; d = c - e * d; c = f;
                }
                c %= p;
                return c + (c < 0 ? p : 0);
            }
            public static long[] Facts(int n, long p)
            {
                var facts = new long[n + 1];
                facts[0] = 1;
                for (int i = 1; i <= n; ++i)
                {
                    facts[i] = (i * facts[i - 1]) % p;
                }
                return facts;
            }
            public static Func<int,int,long> Combin(int n, long P)
            {
                var fact = Facts(n, P);
                var inv = fact.Select(x => Inverse(x, P)).ToArray();
                return (N, M) =>
                {
                    if (N < 0 || N < M || M < 0) { return 0; }
                    return (((fact[N] * inv[M]) % P) * inv[N - M]) % P;
                };
            }
        }

        static string ReadStr()
        {
            return Console.ReadLine();
        }
        static string[] ReadStrN()
        {
            return Console.ReadLine().Split(' ');
        }
        static int ReadInt()
        {
            return Convert.ToInt32(Console.ReadLine());
        }
        static int[] ReadIntN()
        {
            return Console.ReadLine().Split(' ').Select(s => Convert.ToInt32(s)).ToArray();
        }
        static long ReadLong()
        {
            return Convert.ToInt64(Console.ReadLine());
        }
        static long[] ReadLongN()
        {
            return Console.ReadLine().Split(' ').Select(s => Convert.ToInt64(s)).ToArray();
        }
        static Tuple<string, string>[] ReadTupleStr(int n)
        {
            List<Tuple<string, string>> list = new List<Tuple<string, string>>();
            for (int i = 0; i < n; ++i)
            {
                var ab = ReadStrN();
                list.Add(new Tuple<string, string>(ab[0], ab[1]));
            }
            return list.ToArray();
        }
        static Tuple<long, long>[] ReadTupleLong(int n)
        {
            List<Tuple<long, long>> list = new List<Tuple<long, long>>();
            for (int i = 0; i < n; ++i)
            {
                var ab = ReadLongN();
                list.Add(new Tuple<long, long>(ab[0], ab[1]));
            }
            return list.ToArray();
        }
        static Tuple<double, double>[] ReadTupleDouble(int n)
        {
            List<Tuple<double, double>> list = new List<Tuple<double, double>>();
            for (int i = 0; i < n; ++i)
            {
                var ab = ReadLongN();
                list.Add(new Tuple<double, double>(ab[0], ab[1]));
            }
            return list.ToArray();
        }
    }
}
