//---------------------------------------------------------------------------

//最終更新日 02/02/08
//bbsmenu.html,bbstable.htmlなどを解析してカテゴリ別に板の一覧を作成するクラスです。
//純粋にC++のみで作成していますのでLinuxなどでもこのままコンパイルすれば使えると思います。
//このクラスをそのまま使うには"bbsmenu.html"が実行ファイルと同じ
//フォルダにある必要がありますが、簡単に変更できます。(後述)

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
#pragma hdrstop

//---------------------------------------------------------------------------

//カテゴリーの名前とその位置を格納する
struct Category{
   string c_str;
   int c_pos;
};

//板のサーバ、名前(英語、日本語)とその位置を格納する
struct BoardList{
   string server;
   string board_E;
   string board_J;
   int b_pos;
};

class BbsMenu{
private:
   string str_all;
   Category *c_m;
   BoardList *bl_m;
public:
   BbsMenu(int,int);
   bool Storage2string(char*);
   int TagLSCheck();
   int StorageCategory(string,string);
   int StorageBoard();
   void BoardClassify(int,int);
   void AllOutput(int,int);
   void Make2chbrd(int,int);
   void Makeboard2ch(int,int);
   ~BbsMenu();
};

BbsMenu::BbsMenu(int cat_n,int board_n){
   c_m = new Category[cat_n];
   bl_m = new BoardList[board_n];
}

BbsMenu::~BbsMenu(){
   delete []c_m;
   delete []bl_m;
}

//渡されたファイル名のファイルの内容をすべてstr_allに入れます。
//ここではbbsmenu.htmlの中身をstr_allに入れればいいので、
//ネットから直接ダウンロードして入れたりしてもOKです。
bool BbsMenu::Storage2string(char* filename){
   ifstream ifs(filename);
   if(!ifs) return 1;

   string str;
   while(!ifs.eof()){
      getline(ifs,str);
      str_all.append(str,0,str.length()-1);
   }
   return 0;
}

//使われているタグが大文字か小文字かを判定し、StorageCategoryにタグを渡します。
int BbsMenu::TagLSCheck(){
   if(str_all.find("<B>") == -1){
      if(str_all.find("<b>") != -1){
          return StorageCategory("<b>","</b>");
      }
      else return 0;
   }
   return StorageCategory("<B>","</B>");
}

//渡されたタグに囲まれた文字列を(「案内」など)Category.c_strに入れます。
//その文字列がある位置をCategory.c_posに入れます。
//また、カテゴリーの数(関係ないカテゴリ含む)を返します。
int BbsMenu::StorageCategory(string lbt_s_str,string lbt_l_str){
   unsigned int i,counter_i;
   int start_i,end_i,nagasa_i;
   const int lb_size = lbt_s_str.length();

   for(i=0,counter_i=0;i<str_all.length();i++){
      if((start_i=str_all.find(lbt_s_str,i)) != -1 &&
      (end_i=str_all.find(lbt_l_str,i)) != -1){
         if((nagasa_i=end_i - (start_i+lb_size)) > 0){
            c_m[counter_i].c_str = str_all.substr(start_i+lb_size,nagasa_i);
            i=c_m[counter_i].c_pos=end_i+1;
            counter_i++;
      }}
      else break;
   }
   return counter_i;
}

//BoardList.server(「tako.2ch.net」など)、BoardList.board_E(「news」など)
//BoardList.board_J(「ロビー」など)を入れます。
//その文字列がある位置をBoardList.b_posに入れます。
//また、板の数(関係ない板含む)を返します。
int BbsMenu::StorageBoard(){
   unsigned int i,counter_i;
   int start_i,end_i,nagasa_i,backslash_i,centbacks_i,linktarget_i;
   string http_str = "http://";
   const int http_size = http_str.length();

   for(i=0,counter_i=0;i<str_all.length();i++){
      if((start_i=str_all.find(http_str,i)) != -1){
         if((end_i=str_all.find('>',start_i)) != -1){
            backslash_i=str_all.rfind('/',end_i);
            centbacks_i=str_all.rfind('/',backslash_i-1);
            if((linktarget_i=str_all.find("</",end_i)) != -1){
               if(start_i + http_size < centbacks_i){
                  bl_m[counter_i].server=str_all.substr(start_i+http_size,centbacks_i-start_i-http_size);
                  bl_m[counter_i].board_E=str_all.substr(centbacks_i+1,backslash_i-centbacks_i-1);
                  bl_m[counter_i].board_J=str_all.substr(end_i+1,linktarget_i-end_i-1);
                  bl_m[counter_i].b_pos=start_i;
                  counter_i++;
               }
               i=linktarget_i;
            }
            else return 0;
      }}
      else break;
   }
   return counter_i;
}

//この処理が終わると、例えば
//Category.c_str=社会 Category.c_pos=4
//BoardList.server=news.2ch.net BoardList.board_E=news
//BoardList.board_J=ニュース速報 BoardList.b_pos=4
//のように格納されます。
void BbsMenu::BoardClassify(int category_n,int ita_n){
   int i,j;

   for(i=0,j=0;i<ita_n && j<category_n;i++){
      if(c_m[j].c_pos < bl_m[i].b_pos && bl_m[i].b_pos < c_m[j+1].c_pos){
         bl_m[i].b_pos = j;
      }
      else{
         if(bl_m[i].b_pos > c_m[j+1].c_pos){
            c_m[j].c_pos = j;
            j++;
            i--;
   }}}
}

//ここは構造体にどのように格納されているのかを理解してもらうために、
//格納されているすべての文字列などを出力するための関数です。
//実行して出来た"kakunin.txt"を開いてみて理解を深めてください。
void BbsMenu::AllOutput(int category_n,int ita_n){
   ofstream ofs("kakunin.txt");
   int i;

   ofs<<"Category.c_str,Category.c_posに格納されているもの一覧"<<endl;
   for(i=0;i<category_n;i++)
      ofs<<i<<':'<<c_m[i].c_str<<'\t'<<c_m[i].c_pos<<endl;
   ofs<<endl<<"BoardList.server,board_E,board_J,b_posに格納されているもの一覧"<<endl;
   for(i=0;i<ita_n;i++)
      ofs<<i<<':'<<bl_m[i].server<<'\t'<<bl_m[i].board_E<<'\t'<<bl_m[i].board_J<<'\t'<<bl_m[i].b_pos<<endl;
}

//これはサンプルです。このように書くとかちゅ〜しゃの2channel.brdが
//作成されます。このクラスを使われる方は、
//基本的にこのサンプルを元にここを変えるだけでいいです。
void BbsMenu::Make2chbrd(int category_n,int ita_n){
   ofstream ofs("2channel.txt");//すぐ開けるように[.brd]ではなく[.txt]にしています
   ofs<<'2'<<endl;//これって何の意味があるんだろ?
   int i,j;
   bool c_write;

   for(i=0,j=0,c_write=false;c_m[j].c_str != "チャット" && i<ita_n;i++){
      if(j == bl_m[i].b_pos){
         if(c_write == false){
            ofs<<c_m[j].c_str<<"\t0"<<endl;
            c_write = true;
         }
         ofs<<'\t'<<bl_m[i].server<<'\t'<<bl_m[i].board_E<<'\t'<<bl_m[i].board_J<<endl;
      }
      else{
         if(bl_m[i].b_pos < category_n){j++;i--;c_write=false;}
   }}
}

//これもサンプルです。ギコナビのboard.2chを作成します。
//INI形式ですが新たにヘッダをインクルードするのが面倒なので
//強引に今ある機能のみでファイルに書き込んでいます。
//ギコナビにはwww.machibbs.comを読み込む機能がまだついていないので、
//machibbsは避けるようにしている点に注目してください。
void BbsMenu::Makeboard2ch(int category_n,int ita_n){
   ofstream ofs("board.txt");//これもすぐ開けるように[.2ch]ではなく[.txt]にしています
   int i,j;
   bool c_write;

   for(i=0,j=0,c_write=false;c_m[j].c_str != "チャット" && i<ita_n;i++){
      if(j == bl_m[i].b_pos){
         if(bl_m[i].server.find("machibbs") == -1){
            if(c_write == false){
               ofs<<endl<<'['<<c_m[j].c_str<<']'<<endl;
               c_write = true;
            }
            ofs<<bl_m[i].board_J<<"=http://"<<bl_m[i].server<<'/'<<bl_m[i].board_E<<'/'<<endl;
      }}
      else{
         if(bl_m[i].b_pos < category_n){j++;i--;c_write=false;}
   }}
}

int main(int argc, char* argv[]){
   BbsMenu catyusha(128,2048);
   if(catyusha.Storage2string("bbsmenu.html") == 1) return 1;
   int category_n,ita_n;
   if((category_n=catyusha.TagLSCheck()) == 0) return 1;
   if((ita_n=catyusha.StorageBoard()) == 0) return 1;
   catyusha.BoardClassify(category_n,ita_n);
   catyusha.AllOutput(category_n,ita_n);
   catyusha.Make2chbrd(category_n,ita_n);
   catyusha.Makeboard2ch(category_n,ita_n);

   return 0;
}
//---------------------------------------------------------------------------