#include <bits/stdc++.h>
using namespace std;
using Int = int64_t;
using Uint = uint64_t;
constexpr long long int mod = 1e9 + 7;
using Container = vector<Int>;
using Grid = vector<vector<char>>;
struct Point {
  Int x, y;
  Point(Int x, Int y) : x(x), y(y) {}
  Point operator+(Point arg) { return Point(x + arg.x, y + arg.y); }
  Point operator-(Point arg) { return Point(x - arg.x, y - arg.y); }
};
bool inrange(Int l, Int r, Int val) { return l <= val && val <= r; }
int main() {
  Int H, W;
  cin >> H >> W;
  Grid grid(H, vector<char>(W));
  for (Int(y) = 0; (y) < (H); (y)++) {
    string S;
    cin >> S;
    for (Int(x) = 0; (x) < (W); (x)++) {
      grid[y][x] = S[x];
    }
  }
  Point vec[4] = {Point(0, -1), Point(-1, 0), Point(0, 1), Point(1, 0)};
  Point s(0, 0);
  Int ans = LLONG_MIN;
  for (Int(y) = 0; (y) < (H); (y)++) {
    for (Int(x) = 0; (x) < (W); (x)++) {
      if (grid[y][x] == '#') continue;
      s = Point(x, y);
      vector<vector<bool>> visited(H, vector<bool>(W, false));
      vector<vector<Int>> dist(H, vector<Int>(W, -1));
      dist[y][x] = 0;
      queue<Point> q;
      q.push(s);
      while (!q.empty()) {
        auto cur = q.front();
        q.pop();
        for (Int(i) = 0; (i) < (4); (i)++) {
          auto next = cur + vec[i];
          if (inrange(0, W - 1, next.x) && inrange(0, H - 1, next.y) &&
              !visited[next.y][next.x] && grid[next.y][next.x] == '.') {
            q.push(next);
            dist[next.y][next.x] = dist[cur.y][cur.x] + 1;
            ans = max(ans, dist[next.y][next.x]);
            visited[next.y][next.x] = true;
          }
        }
      }
    }
  }
  cout << ans << endl;
}
