#define __mode_debug__
/*   ξ
     ll
   ＿ll＿
 ／  ∞  ＼
│*  Ａ  *│
│*  Ｃ  *│
│*  祈  *│
│*  願  *│
│*      *│
 ￣￣￣￣￣    */
//C++14 (GCC 5.4.1)
#include <bits/stdc++.h>
using namespace std;
#ifdef __mode_debug__
    #define DBG
#else
    #define DBG if(false)
#endif

#define LLINF (1LL<<60)
typedef long long ll;
#define mod 1000000007

#define repd(i,a,b) for (int i=(a);i<(b);i++)
#define rep(i,n) repd(i,0,n)
#define rrepd(i,a,b) for(int i=(a);i>=(b);i--)
#define repi(itr,ds) for(auto itr=ds.begin();itr!=ds.end();itr++)

template<typename T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<typename T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

template < typename T > inline string toString( const T &a ) { ostringstream oss; oss << a; return oss.str(); }

//DUMP
//  ostream vector<T> , vector<vector<T>> , set<T> ,queue<T> , priority_queue<T> , pair<T,U> , map<T,U>
template<class T> ostream& operator<<(ostream&os,vector<T>&vec){os<<"{ ";rep(i,vec.size()) os<<((i==0)?"":", ")<<i<<":"<<vec[i];os<<" }";return os;}
template<class T> ostream& operator<<(ostream&os,vector<vector<T>>&vec){os<<"{"<<endl;rep(i,vec.size())os<<"\t   "<<i<<":"<<vec[i]<<((i==vec.size()-1)? "":",")<<endl;os<<"\t }";return os;}
template<class T> ostream& operator<<(ostream&os,set<T>&p){os<<"{ ";repi(itr,p)  os<<((itr==p.begin())?"":", ")<<*itr;os<<" }";return os;}
template<class T> ostream& operator<<(ostream&os,queue<T>&p){queue<T> q(p);os<<"[< "<<(q.empty()?" <]":"");while(!q.empty()){os<<q.front();q.pop();os<< (q.empty() ? " <]" : ", ");}return os;}
template<class T> ostream& operator<<(ostream&os,priority_queue<T>&p){priority_queue<T> q(p);os<<"[< "<<(q.empty()?" <]":"");while(!q.empty()){os<<q.top();q.pop();os<< (q.empty() ? " <]" : ", ");}return os;}
template<class T,class U> ostream& operator<<(ostream&os,pair<T,U>&p){os<<"("<<p.first<<","<<p.second<<")";return os;}
template<class T,class U> ostream& operator<<(ostream&os,map<T,U>&p){os<<"{ ";repi(itr,p)os<<((itr==p.begin())?"":", ")<<itr->first<<":"<<itr->second;os<<" }";return os;}
void dump_f(string &nm,char t){cerr<<endl;}
template<class T,class... Ts> void dump_f(string &nm,char t,T &&x,Ts&&... ts){int l=nm.find_first_of(',');string name(nm,0,l),nx(nm,l+1,-1);if(t=='\n') cerr<<"  "<<name<<"\t: "<<x<<t;else cerr << name<<": "<<x<<","<<t;dump_f(nx,t,forward<Ts>(ts)...);}
template<class... Ts> void dump_m(int &&ln,string &&nm,char t,Ts&&... ts){cerr<<"[ln: "<<ln<<"]"<<t;dump_f(nm,t,forward<Ts>(ts)...);}
#define dump(...) DBG dump_m(__LINE__,#__VA_ARGS__,'\n',__VA_ARGS__)
#define dl(...) DBG dump_m(__LINE__,#__VA_ARGS__,'\t',__VA_ARGS__)

#define lower_bound_idx(V,c) distance(V.begin(),lower_bound(V.begin(),V.end(),c))


template<class T,class Compare = less<T>>
class warshall_floyd{
private:
    int node_num;
    vector<vector<int>> pred;
    vector<vector<T>> c_ab;
    T Ls,Gt;
    bool is_negative;
public:
    warshall_floyd(int node_num) : 
        node_num(node_num),
        pred(node_num+1,vector<int>(node_num+1,INT_MAX)),
        c_ab(node_num,vector<T>(node_num)),
        is_negative(false)
        {
            if(Compare()(0,1)) Ls=std::numeric_limits<T>::lowest(),Gt=std::numeric_limits<T>::max();
            else Ls=std::numeric_limits<T>::max(),Gt=std::numeric_limits<T>::lowest();
            rep(i,node_num) c_ab[i].assign(node_num,Gt);
            rep(i,node_num) pred[i][i]=-1,c_ab[i][i]=0;
        }
    void direct(int from,int to,T cost){
        c_ab[from][to]=cost;
        pred[from][to]=from;
    }
    void calc(void){
        rep(rel,node_num)rep(a,node_num){
            rep(b,node_num){
                if(c_ab[a][rel]==Gt||c_ab[rel][b]==Gt)continue;
                T nc=c_ab[a][rel]+c_ab[rel][b];
                if(Compare()(nc,c_ab[a][b])){
                    c_ab[a][b]=nc;
                    pred[a][b]=pred[rel][b];
                }
            }
        }
        rep(i,node_num) is_negative=is_negative||(c_ab[i][i]!=0);
    }
    T get_cost(int idx_start,int idx_end) const{
        return c_ab[idx_start][idx_end];
    }
    bool has_negative_cycle() const{
        return is_negative;
    }
    bool is_negative_path(int idx_start,int idx_end){
        int node=idx_end;
        if( pred[idx_start][idx_end]==INT_MAX) return false;
        while(c_ab[node][node]==0 &&//負閉路でない
            pred[idx_start][node]<INT_MAX&&//始点から辿れる
            pred[idx_start][node]!=-1){//始点でない
                node=pred[idx_start][node];
            }
        return (c_ab[node][node]!=0);
    }
    vector<int> get_min_path(int idx_start,int idx_end)const{
        int node=idx_end;
        vector<int> vec;
        vec.push_back(node);
        while(c_ab[node][node]==0 &&//負閉路でない
            pred[idx_start][node]<INT_MAX&&//始点から辿れる
            pred[idx_start][node]!=-1){//始点でない
                node=pred[idx_start][node];
                vec.push_back(node);
            }
        return vec;
    }

};

int main() {
    ios::sync_with_stdio(false);//stdoutとcoutの同期解除
    cin.tie(nullptr);//cinとcoutの同期解除

    int H,W;
    cin >>H>>W;
    vector<string> S(H);
    rep(i,H) cin >> S[i];
    
    warshall_floyd<int> X(H*W);
    vector<int> sgc;
    rep(i,H){
        if(S[i][0]=='.')sgc.push_back(i*W);
        repd(j,1,W){
            if(S[i][j]=='.')sgc.push_back(i*W+j);
            if(S[i][j-1]=='.' && S[i][j]=='.'){
                X.direct(i*W+j-1,i*W+j,1);
                X.direct(i*W+j,i*W+j-1,1);
            }
        }
        if(i>0){
            rep(j,W){
                if(S[i-1][j]=='.' && S[i][j]=='.'){
                    X.direct((i-1)*W+j,i*W+j,1);
                    X.direct(i*W+j,(i-1)*W+j,1);
                }
            }
        }
    }
    int ans=0;
    X.calc();
    int N=sgc.size();
    rep(i,N-1)repd(j,i+1,N){
        chmax(ans,X.get_cost(sgc[i],sgc[j]));
    }

    cout << ans <<endl;
    return (0);
}

Measured memory_bytes: 1536