#include <bits/stdc++.h>
using namespace std;
constexpr long long MOD = 1e9 + 7;
int dx[8] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[8] = {0, 1, 0, -1, 1, -1, 1, -1};
long long A, B, C, D, E, F, G, H, N, M, L, K, P, Q, R, W, X, Y, Z;
string S, T;
long long ans = 0;
template <typename T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (T &x : vec) is >> x;
  return is;
}
class Graph {
  struct edge {
    int from, to, cost;
    edge(int from, int to, int cost) : from(from), to(to), cost(cost) {}
  };

 public:
  int verN;
  int edgeN = 0;
  vector<edge> edge_set;
  vector<vector<pair<int, int>>> adj_list;
  Graph(int verN) : verN(verN), adj_list(verN) {}
  void add_directed_edge(int from, int to, int cost = 1) {
    edge_set.push_back(edge(from, to, cost));
    adj_list[from].push_back(make_pair(to, cost));
    edgeN++;
  }
  void add_undirected_edge(int from, int to, int cost = 1) {
    edge_set.push_back(edge(from, to, cost));
    edge_set.push_back(edge(to, from, cost));
    adj_list[from].push_back(make_pair(to, cost));
    adj_list[to].push_back(make_pair(from, cost));
    edgeN += 2;
  }
  bool bellmanford(int start, vector<int> &d) {
    d.assign(verN, (1 << 30));
    for (int i = 0; i < verN; i++) {
      d[i] = (1 << 30);
    }
    d[start] = 0;
    for (int i = 0; i < verN; i++) {
      for (int j = 0; j < edgeN; j++) {
        edge e = edge_set[j];
        if (d[e.from] != (1 << 30) && d[e.to] > d[e.from] + e.cost) {
          d[e.to] = d[e.from] + e.cost;
          if (i == verN - 1) {
            return false;
          }
        }
      }
    }
    return true;
  }
  vector<int> dijkstra(int start) {
    vector<int> d(verN, (1 << 30));
    d[start] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>,
                   greater<pair<int, int>>>
        pq;
    vector<int> used(verN, 0);
    pq.push(make_pair(0, start));
    while (!pq.empty()) {
      int now = pq.top().second;
      pq.pop();
      used[now] = 1;
      for (auto adj_pair : adj_list[now]) {
        int adj = adj_pair.first;
        int dist = adj_pair.second;
        if (used[adj]) continue;
        if (d[adj] > d[now] + dist) {
          d[adj] = d[now] + dist;
          pq.push(make_pair(d[adj], adj));
        }
      }
    }
    return move(d);
  }
  vector<vector<int>> warshallfloyd() {
    vector<vector<int>> d(verN, vector<int>(verN, (1 << 30)));
    for (int i = 0; i < verN; i++) {
      for (auto adj_pair : adj_list[i]) {
        int adj = adj_pair.first;
        int dist = adj_pair.second;
        d[i][adj] = dist;
      }
    }
    for (int k = 0; k < verN; k++) {
      for (int i = 0; i < verN; i++) {
        for (int j = 0; j < verN; j++) {
          if (d[i][k] == (1 << 30) || d[k][j] == (1 << 30)) continue;
          d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
        }
      }
    }
    for (int i = 0; i < verN; i++) {
      for (int j = 0; j < verN; j++) {
        if (d[i][j] == (1 << 30)) {
          d[i][j] = -1;
        }
      }
    }
    return move(d);
  }
};
signed main() {
  cin >> H >> W;
  vector<vector<char>> s(H + 1, vector<char>(W + 1, '#'));
  Graph g(H * W);
  for (int h = 0; h < (int)(H); h++)
    for (int w = 0; w < (int)(W); w++) {
      cin >> s[h][w];
    }
  for (int h = 0; h < (int)(H); h++)
    for (int w = 0; w < (int)(W); w++) {
      if (s[h][w] == '#') {
        continue;
      }
      if (s[h + 1][w] == '.') {
        g.add_undirected_edge(h * W + w, (h + 1) * W + w);
      }
      if (s[h][w + 1] == '.') {
        g.add_undirected_edge(h * W + w, h * W + (w + 1));
      }
    }
  auto d = g.warshallfloyd();
  for (int i = 0; i < (int)(H * W); i++) {
    ans = max(int(ans), *max_element((d[i]).begin(), (d[i]).end()));
    ;
  }
  cout << ans << "\n";
}
