#include <bits/stdc++.h>
using namespace std;
struct edge {
  long long to, cost;
};
void readInit(int &H, int &W, vector<vector<edge> > &graph,
              vector<vector<long long> > &A);
void dijkstra(int s, vector<vector<edge> > &graph, vector<long long> &d);
int main() {
  int H, W;
  vector<vector<edge> > graph(80);
  vector<vector<long long> > minD(80, vector<long long>(80));
  cin >> H >> W;
  vector<vector<long long> > A(H, vector<long long>(W));
  readInit(H, W, graph, A);
  long long ans = 0;
  for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
      int index = i * W + j;
      if (A[i][j] == 1) continue;
      for (int k = 0; k < H; k++) {
        for (int l = 0; l < W; l++) {
          int goal = k * W + l;
          if (A[k][l] == 1) continue;
          dijkstra(index, graph, minD[index]);
          long long cost = minD[index][goal];
          ans = max(ans, cost);
        }
      }
    }
  }
  cout << ans << endl;
}
void readInit(int &H, int &W, vector<vector<edge> > &graph,
              vector<vector<long long> > &A) {
  for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
      char tmp;
      cin >> tmp;
      if (tmp == '.')
        A[i][j] = 0;
      else
        A[i][j] = 1;
    }
  }
  for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
      if (i == 0) {
        if (j == 0) {
          if (A[i][j + 1] == 0) {
            edge e1 = {j + 1, 1};
            graph[j].push_back(e1);
          }
          if (A[i + 1][j] == 0) {
            edge e2 = {W + j, 1};
            graph[j].push_back(e2);
          }
        } else if (j == W - 1) {
          if (A[i][j - 1] == 0) {
            edge e1 = {j - 1, 1};
            graph[j].push_back(e1);
          }
          if (A[i + 1][j] == 0) {
            edge e2 = {W + j, 1};
            graph[j].push_back(e2);
          }
        } else {
          if (A[i][j - 1] == 0) {
            edge e1 = {j - 1, 1};
            graph[j].push_back(e1);
          }
          if (A[i][j + 1] == 0) {
            edge e2 = {j + 1, 1};
            graph[j].push_back(e2);
          }
          if (A[i + 1][j] == 0) {
            edge e3 = {W + j, 1};
            graph[j].push_back(e3);
          }
        }
      } else if (i == H - 1) {
        if (j == 0) {
          if (A[i][j + 1] == 0) {
            edge e1 = {W * i + j + 1, 1};
            graph[W * i].push_back(e1);
          }
          if (A[i - 1][j] == 0) {
            edge e2 = {W * (i - 1) + j, 1};
            graph[W * i].push_back(e2);
          }
        } else if (j == W - 1) {
          if (A[i][j - 1] == 0) {
            edge e1 = {W * i + j - 1, 1};
            graph[W * i + j].push_back(e1);
          }
          if (A[i - 1][j] == 0) {
            edge e2 = {W * (i - 1) + j, 1};
            graph[W * i + j].push_back(e2);
          }
        } else {
          if (A[i][j - 1] == 0) {
            edge e1 = {W * i + j - 1, 1};
            graph[W * i + j].push_back(e1);
          }
          if (A[i][j + 1] == 0) {
            edge e2 = {W * i + j + 1, 1};
            graph[W * i + j].push_back(e2);
          }
          if (A[i - 1][j] == 0) {
            edge e3 = {W * (i - 1) + j, 1};
            graph[W * i + j].push_back(e3);
          }
        }
      } else {
        if (j == 0) {
          if (A[i][j + 1] == 0) {
            edge e1 = {W * i + j + 1, 1};
            graph[W * i + j].push_back(e1);
          }
          if (A[i - 1][j] == 0) {
            edge e2 = {W * (i - 1) + j, 1};
            graph[W * i + j].push_back(e2);
          }
          if (A[i + 1][j] == 0) {
            edge e3 = {W * (i + 1) + j, 1};
            graph[W * i + j].push_back(e3);
          }
        } else if (j == W - 1) {
          if (A[i][j - 1] == 0) {
            edge e1 = {W * i + j - 1, 1};
            graph[W * i + j].push_back(e1);
          }
          if (A[i - 1][j] == 0) {
            edge e2 = {W * (i - 1) + j, 1};
            graph[W * i + j].push_back(e2);
          }
          if (A[i + 1][j] == 0) {
            edge e3 = {W * (i + 1) + j, 1};
            graph[W * i + j].push_back(e3);
          }
        } else {
          if (A[i][j - 1] == 0) {
            edge e1 = {W * i + j - 1, 1};
            graph[W * i + j].push_back(e1);
          }
          if (A[i][j + 1] == 0) {
            edge e2 = {W * i + j + 1, 1};
            graph[W * i + j].push_back(e2);
          }
          if (A[i - 1][j] == 0) {
            edge e3 = {W * (i - 1) + j, 1};
            graph[W * i + j].push_back(e3);
          }
          if (A[i + 1][j] == 0) {
            edge e4 = {W * (i + 1) + j, 1};
            graph[W * i + j].push_back(e4);
          }
        }
      }
    }
  }
}
void dijkstra(int s, vector<vector<edge> > &graph, vector<long long> &d) {
  priority_queue<pair<int, int>, vector<pair<int, int> >,
                 greater<pair<int, int> > >
      que;
  for (int i = 0; i < d.size(); i++) d[i] = 1000;
  d[s] = 0;
  que.push(pair<int, int>(0, s));
  while (!que.empty()) {
    pair<int, int> tmpP = que.top();
    que.pop();
    int v = tmpP.second;
    if (d[v] < tmpP.first) continue;
    for (int i = 0; i < graph[v].size(); i++) {
      edge e = graph[v][i];
      if (d[e.to] > d[v] + e.cost) {
        d[e.to] = d[v] + e.cost;
        que.push(pair<int, int>(d[e.to], e.to));
      }
    }
  }
}
