#include <bits/stdc++.h>
using i64 = int_fast64_t;
namespace edamat {
template <class Info>
struct edge {
  int from, to;
  Info info;
  edge(int to, Info info) : to(to), info(info) {}
  edge(int from, int to, Info info) : from(from), to(to), info(info) {}
};
struct normal_edge {
  normal_edge() {}
};
template <class Info>
class graph {
 protected:
  using edge_type = edge<Info>;
  ::std::size_t N;
  ::std::vector<::std::vector<edge_type>> edges;

 public:
  graph(::std::size_t N_) : N(N_), edges(N_) {}
  virtual void add_edge(int x, int y, Info info = static_cast<Info>(1)) = 0;
  const ::std::vector<edge_type> &delta(int v) const {
    assert(v < N);
    return edges[v];
  }
  const ::std::vector<edge_type> &operator[](int v) const {
    return this->delta(v);
  }
  ::std::size_t size() const noexcept { return N; }
  ::std::vector<Info> bfs(int);
  ::std::vector<Info> dijkstra(int);
  ::std::vector<Info> bellman_ford(int);
  ::std::vector<::std::vector<Info>> warshall_floyd(void);
};
template <class Info>
class undirected_graph : public graph<Info> {
 public:
  undirected_graph(::std::size_t N) : graph<Info>(N) {}
  void add_edge(int x, int y, Info info = static_cast<Info>(1)) {
    this->edges[x].push_back(edge<Info>(x, y, info));
    this->edges[y].push_back(edge<Info>(y, x, info));
  }
};
template <class Info>
class directed_graph : public graph<Info> {
 public:
  directed_graph(::std::size_t N) : graph<Info>(N) {}
  void add_edge(int x, int y, Info info = static_cast<Info>(1)) {
    this->edges[x].push_back(edge<Info>(x, y, info));
  }
  ::std::vector<int> topological_sort(void);
};
template <class Info>
class grid_graph : public graph<Info> {
 public:
  ::std::size_t H, W;
  grid_graph(::std::size_t H, ::std::size_t W)
      : graph<Info>(H * W), H(H), W(W) {}
  void add_edge(int x, int y, Info info = static_cast<Info>(1)) {
    this->edges[x].push_back(edge<Info>(x, y, info));
  }
  void add_edge(int i, int j, int ni, int nj,
                Info info = static_cast<Info>(1)) {
    add_edge(to_int(i, j), to_int(ni, nj), info);
  }
  inline int to_int(int i, int j) { return i * W + j; };
  inline std::pair<int, int> to_grid(int n) {
    return std::pair<int, int>{n / W, n % W};
  };
  void make_graph(std::vector<std::vector<bool>> &A) {
    for (int i = 0; i < H; ++i) {
      for (int j = 0; j < W; ++j) {
        for (int di = -1; di <= 1; ++di) {
          for (int dj = -1; dj <= 1; ++dj) {
            if (abs(di) + abs(dj) == 1) {
              int ni = i + di, nj = j + dj;
              if (ni < 0 or ni >= H or nj < 0 or nj >= W) {
                continue;
              }
              if (A[i][j] and A[ni][nj]) {
                add_edge(i, j, ni, nj);
                add_edge(ni, nj, i, j);
              }
            }
          }
        }
      }
    }
  }
};
template <typename Info>
::std::vector<Info> graph<Info>::bfs(int s) {
  const auto INF = ::std::numeric_limits<Info>::max();
  ::std::vector<Info> dist(edges.size(), INF);
  ::std::vector<int> b(edges.size(), -1);
  ::std::queue<Info> q;
  q.push(s);
  dist[s] = 0;
  while (!q.empty()) {
    Info v = q.front();
    q.pop();
    for (auto e : edges[v]) {
      if (dist[e.to] != INF) continue;
      dist[e.to] = dist[e.from] + e.info;
      b[e.to] = e.from;
      q.push(e.to);
    }
  };
  return dist;
}
template <typename Info>
::std::vector<Info> graph<Info>::dijkstra(int s) {
  const auto INF = ::std::numeric_limits<Info>::max();
  ::std::vector<Info> dist(edges.size(), INF);
  ::std::vector<int> b(edges.size(), -1);
  using Pi = ::std::pair<Info, int>;
  ::std::priority_queue<Pi, std::vector<Pi>, std::greater<Pi>> que;
  dist[s] = 0;
  que.emplace(dist[s], s);
  while (!que.empty()) {
    Info cost;
    int idx;
    ::std::tie(cost, idx) = que.top();
    que.pop();
    if (dist[idx] < cost) continue;
    for (auto &e : edges[idx]) {
      auto next_cost = cost + e.info;
      if (dist[e.to] <= next_cost) continue;
      dist[e.to] = next_cost;
      b[e.to] = idx;
      que.emplace(dist[e.to], e.to);
    }
  }
  return dist;
};
template <typename Info>
::std::vector<Info> graph<Info>::bellman_ford(int s) {
  const auto INF = ::std::numeric_limits<Info>::max();
  ::std::vector<Info> dist(edges.size(), INF);
  ::std::vector<int> b(edges.size(), -1);
  dist[s] = 0;
  for (int i = 0; i < edges.size() - 1; i++) {
    for (int idx = 0; idx < edges.size(); idx++) {
      for (auto &e : edges[idx]) {
        if (dist[e.from] == INF) continue;
        auto next_cost = dist[e.from] + e.info;
        if (dist[e.to] <= next_cost) continue;
        dist[e.to] = next_cost;
        b[e.to] = e.from;
      }
    }
  }
  for (int idx = 0; idx < edges.size(); idx++) {
    for (auto &e : edges[idx]) {
      if (dist[e.from] == INF) continue;
      if (dist[e.from] + e.info < dist[e.to]) return ::std::vector<Info>();
    }
  }
  return dist;
}
template <typename Info>
::std::vector<::std::vector<Info>> graph<Info>::warshall_floyd() {
  const auto INF = ::std::numeric_limits<Info>::max();
  ::std::vector<::std::vector<Info>> dist(
      edges.size(), ::std::vector<Info>(edges.size(), INF));
  for (int idx = 0; idx < edges.size(); idx++) {
    for (auto &e : edges[idx]) {
      dist[e.from][e.to] = e.info;
    }
  }
  for (int k = 0; k < edges.size(); k++) {
    for (int i = 0; i < edges.size(); i++) {
      for (int j = 0; j < edges.size(); j++) {
        if (dist[i][k] == INF || dist[k][j] == INF) continue;
        dist[i][j] = ::std::min(dist[i][j], dist[i][k] + dist[k][j]);
      }
    }
  }
  return dist;
}
template <typename Info>
::std::vector<int> directed_graph<Info>::topological_sort() {
  ::std::vector<int> h(this->size(), 0);
  for (int idx = 0; idx < this->size(); idx++) {
    for (auto &e : this->edges[idx]) {
      h[e.to]++;
    }
  }
  ::std::stack<int> st;
  for (int i = (0); (i) < (this->size()); ++(i))
    if (h[i] == 0) st.push(i);
  ::std::vector<int> ret;
  while (st.size()) {
    int i = st.top();
    st.pop();
    ret.emplace_back(i);
    for (auto &e : this->edges[i]) {
      h[e.to]--;
      if (h[e.to] == 0) st.push(e.to);
    }
  }
  return ret;
}
}  // namespace edamat
void solve() {
  int H, W;
  std::cin >> H >> W;
  std::vector<std::vector<bool>> A(H, std::vector<bool>(W));
  for (int h = (0); (h) < (H); ++(h)) {
    std::string s;
    std::cin >> s;
    for (int w = (0); (w) < (W); ++(w)) {
      A[h][w] = (s[w] == '.');
    }
  }
  edamat::grid_graph<int> g(H, W);
  g.make_graph(A);
  int ans = -1;
  auto d = g.warshall_floyd();
  for (int i = (0); (i) < (H * W); ++(i)) {
    for (auto x : d[i]) {
      if (x >= ans and x != std::numeric_limits<int>::max()) {
        ans = x;
      }
    }
  }
  std::cout << ans << std::endl;
}
int main() {
  std::cin.tie(nullptr);
  solve();
  return 0;
}
