import std.algorithm;
import std.array;
import std.conv;
import std.math;
import std.range;
import std.stdio;
import std.string;
import std.typecons;

alias C = Tuple!(int, int);
immutable C[] dirs = [
  tuple (1, 0),
  tuple (-1, 0),
  tuple (0, 1),
  tuple (0, -1)
];

enum inf = int.max; 

void main() {
  auto s = readln.strip.splitter;
  immutable int h = s.front.to!int;s.popFront;
  immutable int w = s.front.to!int;
  auto a = new char[][h];
  foreach (j; 0 .. h) {
    auto z = readln;
    a[j] = z[0 .. w].dup;
  }
  auto d = new int[][](h, w);
  void clear () {
    foreach (j; 0 .. h) foreach (i; 0 .. w) d[j][i] = inf;
  }
  C[] bfs (in int sy, in int sx, out int dy, out int dx, out int dist) {
    d[sy][sx] = 0;
    C[] q;
    q ~= C (sy, sx);
    int left = 0;
    while (left < q.length) {
      auto t = q[left++];
      immutable int y = t[0], x = t[1];
      dy = y;
      dx = x;
      foreach (const ref p; dirs) {
        immutable j = y + p[0];
        if (j < 0 || j >= h) continue;
        immutable i = x + p[1];
        if (i < 0 || i >= w) continue;
        if (d[j][i] < inf || a[j][i] == '#') continue;
        q ~= C (j, i);
        d[j][i] = d[y][x] + 1;
      }
    }
    dist = d[dy][dx];
    return q;
  }
  int res;
  foreach (j; 0 .. h) foreach (i; 0 .. w) if (a[j][i] != '#') {
    int y, x, dd;
    clear ();
    bfs (j, i, y, x, dd); 
    res = max (res, dd);
  }
  /*
  C[] u;
  clear ();
  foreach (j; 0 .. h) foreach (i; 0 .. w) if (a[j][i] != '#' && d[j][i] == inf) {
    int y, x, dd;
    bfs (j, i, y, x, dd); 
    u ~= tuple (y, x);
  }
  clear ();
  int res;
  foreach (p; u) {
    int y, x, dd;
    res = max (res, dd);
  }
  */
  writeln (res);
}

