#include <iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<cmath>
#include<sstream>
#include<set>
#include<map>
#include<numeric>
#include<limits>
#include <cstdint>
#include<stack>
#include<queue>
using namespace std;
#define ll long long int
#define rep(N) for(ll i=0;i<N;i++)
#define rep2(N1,N2) for(ll i=0;i<N1;i++)for(ll j=0;j<N2;j++)
#define rep3(N1,N2,N3)for(ll i=0;i<N1;i++)for(ll j=0;j<N2;j++)for(ll k=0;k<N3;k++)
#define For(i,N) for(ll i=0;i<N;i++)

void yn(bool b) {
	if (b)printf("yes\n");
	else printf("no\n");
}

void Yn(bool b) {
	if (b)printf("Yes\n");
	else printf("No\n");
}

void YN(bool b) {
	if (b)printf("YES\n");
	else printf("NO\n");
}

int ctoi(char c) {
	return c - '0';
}

ll kai(ll x) {
	ll a = 1;
	if (x <= 0)
		return 1;
	else {
		for (ll i = 0; i < x; i++)
			a = a * (i + 1);
		return a;
	}
}

ll rui(ll x, ll y) {
	ll a = 1;
	for (ll i = 0; i < y; i++)
		a = a * x;
	return a;
}
const int nmax = 150;
int cccc[nmax][nmax];
ll comb(ll n, ll k) {
	if (n < k) {
		cout << "error n<k" << endl;
		return -1;
	}
	if (cccc[n][k] > 0)return cccc[n][k];
	if (k == 0 || n == k)return 1;
	return cccc[n][k] = comb(n - 1, k - 1) + comb(n - 1, k);
}

ll keta(ll a) {
	ll i = 1;
	while (1) {
		if (a < pow((ll)10, i))
			return i;
		i++;
	}
}
vector<ll> v;
ll n, m;
//m進数全探索
ll inc(ll a = n - 1) {
	if (a == 0 && v[0] == m) {
		v[0] = -1;
		return v[0];
	}
	if (v[a] == m) {
		v[a] = 0;
		inc(a - 1);
		return v[a];
	}
	else {
		++v[a];
		return v[a];
	}
}

ll mod = 1000000007;

ll modpowl(ll a, ll b) {
	if (b == 0) {

		return 1;
	}
	if (b == 1) {

		return a;
	}
	if (b >= 2) {
		if (b % 2 == 0) {
			ll c = modpowl((a*a) % mod, b / 2);
			c = c % mod;

			return c;
		}
		else {
			ll c = (modpowl((a*a) % mod, b / 2) % mod)*a;
			c = c % mod;

			return c;
		}
	}
}
ll llpow(ll a, ll b) {
	if (b == 0) {

		return 1;
	}
	if (b == 1) {

		return a;
	}
	if (b >= 2) {
		if (b % 2 == 0) {
			ll c = llpow((a*a), b / 2);
			c = c;

			return c;
		}
		else {
			ll c = (llpow((a*a), b / 2))*a;
			c = c;

			return c;
		}
	}
}
std::vector<ll> Eratosthenes(ll N)
{
	std::vector<bool> is_prime(N + 1);
	for (ll i = 0; i <= N; i++)
	{
		is_prime[i] = true;
	}
	std::vector<ll> P;
	for (ll i = 2; i <= N; i++)
	{
		if (is_prime[i])
		{
			for (ll j = 2 * i; j <= N; j += i)
			{
				is_prime[j] = false;
			}
			P.emplace_back(i);
		}
	}
	return P;
}

int main() {
	cout.precision(20);
	ll H, W;
	ll ans = 0;
	cin >>  H >> W;
	vector<string>S(H);
	rep(H)
		cin >> S[i];
	vector<vector<vector<pair<int,int>>>>L;
	L.resize(H);
	rep(H)
		L[i].resize(W);
	rep2(H, W) {
		if (i != 0)L[i][j].push_back({ i - 1,j });
		if (i != H - 1)L[i][j].push_back({ i + 1,j });
		if (j != 0)L[i][j].push_back({ i, j - 1 });
		if (j != W-1)L[i][j].push_back({ i, j + 1 });
	}
	
	queue<pair<int,int>> todo;
	vector<vector<int>>seen;
	vector<vector<int>>depth;
	seen.resize(H); depth.resize(H);
	rep(H)
		seen[i].resize(W);
	rep(H)
		depth[i].resize(W);
	rep2(H,W){
		if(S[i][j]=='.')
	todo.push({ i,j });
	
	while (!todo.empty()) {
		pair<int, int>a = todo.front();
		seen[a.first][a.second] = 1;
		For(k, L[a.first][a.second].size()) {
			if (seen[L[a.first][a.second][k].first][L[a.first][a.second][k].second] == 0 && S[L[a.first][a.second][k].first][L[a.first][a.second][k].second] == '.') {
				todo.push(L[a.first][a.second][k]);
				seen[L[a.first][a.second][k].first][L[a.first][a.second][k].second] = 1;
				depth[L[a.first][a.second][k].first][L[a.first][a.second][k].second] = depth[a.first][a.second] + 1;
				if (ans < depth[L[a.first][a.second][k].first][L[a.first][a.second][k].second])
					ans = depth[L[a.first][a.second][k].first][L[a.first][a.second][k].second];
			}
		}
		todo.pop();
		
	}
	For(p,H)
		For(pp, W) {
		seen[p][pp] = 0;
		depth[p][pp] = 0;
	}
	}
	cout << ans;
}

Measured memory_bytes: 256