#include <bits/stdc++.h>
using namespace std;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
using Point_t = struct Point {
  int x;
  int y;
};
void init_searched(bool **is_found, int height, int width);
int main(void) {
  int height;
  int width;
  cin >> height;
  cin >> width;
  vector<string> maze;
  for (int i = 0; i < height; i++) {
    string maze_line;
    cin >> maze_line;
    maze.push_back(maze_line);
  }
  queue<Point_t> search_que;
  bool **is_found;
  is_found = new bool *[height];
  for (int i = 0; i < height; i++) {
    is_found[i] = new bool[width];
  }
  int max_dist = 0;
  for (int h = 0; h < height; h++) {
    for (int w = 0; w < width; w++) {
      Point_t start_point = {w, h};
      if (maze[h][w] == '#') {
        continue;
      }
      init_searched(is_found, height, width);
      search_que.push(start_point);
      is_found[h][w] = true;
      int max_d = 0;
      while (!search_que.empty()) {
        int current_size = search_que.size();
        for (int que_index = 0; que_index < current_size; que_index++) {
          Point_t current_point = search_que.front();
          search_que.pop();
          for (int dir = 0; dir < 4; dir++) {
            int x = dx[dir] + current_point.x;
            int y = dy[dir] + current_point.y;
            if (x >= 0 && x < width && y >= 0 && y < height) {
              if (maze[y][x] == '.') {
                if (!is_found[y][x]) {
                  search_que.push(Point_t{x, y});
                  is_found[y][x] = true;
                }
              }
            }
          }
        }
        max_d += 1;
      }
      max_d -= 1;
      if (max_dist < max_d) {
        max_dist = max_d;
      }
    }
  }
  cout << max_dist << endl;
  for (int i = 0; i < height; i++) {
    delete[] is_found[i];
  }
  delete[] is_found;
  return 0;
}
void init_searched(bool **is_found, int height, int width) {
  for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
      is_found[i][j] = false;
    }
  }
}
