// The very basic /////////////////////////////////////////////////////////////
#include <bits/stdc++.h>
using namespace std;
#define ALL_H 1

// Debug it by D(x) ///////////////////////////////////////////////////////////
#define DEBUG 1
#if DEBUG
#define D(x) x
#else
#define D(x)
#endif

// Print elements of containers using << operator /////////////////////////////
// pair
template<typename T1, typename T2> ostream& operator<<(ostream& s, const pair<T1, T2>& p) {
  return s << "(" << p.first << ", " << p.second << ")";}
// vector
template<typename T> ostream& operator<<(ostream& s, const vector<T>& v) {
  for (auto &elem : v) {
    s << elem;
    if (&elem != &v.back()) s << " ";
  }
  return s;
}
// 2 dimentional vector
template<typename T> ostream& operator<<(ostream& s, const vector< vector<T> >& vv) {
  for (auto &elem : vv) {
    s << elem;
    if (&elem != &vv.back()) s << endl;
  }
  return s;
}
// map
template<typename T1, typename T2> ostream& operator<<(ostream& s, const map<T1, T2>& m) {
  s << "{" << endl;
  for (auto &elem: m) {
    s << " " << elem.first << " : " << elem.second << endl;
  }
  s << "}";
  return s;
}
//set
template<typename T> ostream& operator<<(ostream& s, const set<T>& m) {
  s << "{ ";
  for (auto &elem: m) {
    s << elem << " ";
  }
  s << "}";
  return s;
}

// Convenient macros //////////////////////////////////////////////////////////

// types
#define ll long long
#define ld long double
#define int ll
#define Pi pair<int, int>
#define Graph vector<vector<int>>
#define WeightGraph vector<vector<Pi>>
#define INF LLONG_MAX/100
// other
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define all(x) (x).begin(),(x).end()
#define perm(c) sort(all(c));for(bool c##p=1;c##p;c##p=next_permutation(all(c)))

// update max or min //////////////////////////////////////////////////////////

template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

// gcd
int gcd(int a,int b){return b?gcd(b,a%b):a;}

// for grids
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};

///////////////////////////////////////////////////////////////////////////////
//                             Start Coding Here                             //
///////////////////////////////////////////////////////////////////////////////


static const int MAX = 400;

int d[MAX][MAX];

// O(N^3)
void floyd(int (&d)[MAX][MAX], const int& n) {

  //  経由地点k, スタートi,  目的地jを, 右から(jから)走破していく.
  for (int k = 0; k < n; k++) {
    for (int i = 0; i < n; i++) {
      if (d[i][k] == INF) continue;
      for (int j = 0; j < n; j++) {
        if (d[k][j] == INF) continue;
        d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
      }
    }
  }
}


signed main() {
  vector<string> field;
  int N, M; cin >> N >> M;
  rep(i, N) {
    string x; cin >> x;
    field.push_back(x);
  }

  for (int i = 0; i < N*M; i++) {
    for (int j = 0; j < N*M; j++) {
      d[i][j] = (i == j ? 0 : INF);
    }
  }

  rep(i, N) {
    rep(j, M) {
      if (field[i][j] == '.'){
        rep(k, 4) {
          int nx = i + dx[k];
          int ny = j + dy[k];
          if (nx >= 0 && nx < N && ny >= 0 && ny < M) {
            if (field[nx][ny] == '.') {
              d[i*M+j][nx*M+ny] = 1;
              d[nx*M+ny][i*M+j] = 1;
            }
          }
        }
      }
    }
  }

  floyd(d, N*M);
  // rep(i,N*M) {
  //   rep(j, N*M) {
  //     cout << d[i][j] << endl;
  //   }
  // }
  int ans = -1;
  rep(i, N*M) {
    rep(j, N*M) {
      if (d[i][j] != INF) {
        chmax(ans, d[i][j]);
      }
    }
  }
  cout << ans << endl;  
}


Measured memory_bytes: 1664