TopCoder SRM のカレンダー

TopCoder SRM の日程を表示するプログラムを組みました.
Web上で動くほうがいいので,そのうち Action Script か PHP で書き直そうかな.

#include <windows.h>
#include <wininet.h>
#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <conio.h>
using namespace std;

#pragma comment(lib, "wininet.lib")

#define AGENT_INPUT "INPUT"

class srmInfo
{
	string m_name;	//	SRM の名前
	string m_time;	//	SRM の日時,MMDDhhmm,072400(7月24日0時0分)
public:
	void addName(string s) { m_name += s; }	//	名前を追加
	void setTime(string s) { m_time = s; }	//	時間を設定
	void disp();							//	情報を表示
};

//---------------------------------------------------------
//	SRM 情報を表示する
//---------------------------------------------------------
void srmInfo::disp()
{
	bool is_overD = false;	//	日を超えるか
	bool is_overM = false;	//	月を超えるか

	int i = atoi(m_time.c_str());	//	整数で考える

	//	時
	if(i%100 < 11)	//	NY時 -> 日本時
	{
		i += 13;
	}
	else
	{
		i -= 11;
		i += 100;
		is_overD = true;
	}

	//	日
	if(is_overD)
	{
		int end;
		int mon = i / 10000;
		if(mon == 4 || mon == 6 || mon == 9 || mon == 11)
		{
			end = 30;
		}
		else if(mon == 2)
		{
			//	ローカルタイムの取得
			time_t    t, jp_t;
			struct tm jp_tm;
			time(&t);
			localtime_s(&jp_tm, &t);
			jp_t  = mktime(&jp_tm);

			int year = jp_tm.tm_year + 1900;
			cout << year << endl;

			if(((year%400) == 0) || (((year%100) != 0) && ((year%4) == 0)))
			{
				end = 29;
			}
			else
			{
				end = 28;
			}
		}
		else
		{
			end = 31;
		}

		if((i/100)%100 >= end)
		{
			i -= end * 100;
			i += 10000;
			is_overM = true;
		}
	}

	//	年
	if(is_overM && i/10000 == 13)
	{
		i -= 120000;
		m_time.at(0) = '0';
	}

	for(int j = m_time.size()-1; j >= 0; j--)
	{
		m_time.at(j) = '0' + i%10;
		i /= 10;
	}

	cout << m_name << endl;
	for(unsigned int i = 0; i < m_time.size(); i++)
	{
		cout << m_time[i];
		switch(i)
		{
		case 1:
			cout << "月 ";
			break;
		case 3:
			cout << "日 ";
			break;
		case 5:
			cout << "時 ";
			break;
		}
	}
	cout << endl;
}

//---------------------------------------------------------
//	SRM 情報をセットする
//	引数
//		v_tag:	タグから抜き出した情報
//		v_srm:	SRM 情報
//		offset:	SRM 情報を追加する場所の最初
//---------------------------------------------------------
void setSRM(vector<string> *v_tag, vector<srmInfo> *v_srm, int offset)
{
	vector<string> v_mon;	//	月のリスト
	v_mon.push_back("offset");
	v_mon.push_back("January");
	v_mon.push_back("February");
	v_mon.push_back("March");
	v_mon.push_back("April");
	v_mon.push_back("May");
	v_mon.push_back("June");
	v_mon.push_back("July");
	v_mon.push_back("August");
	v_mon.push_back("September");
	v_mon.push_back("October");
	v_mon.push_back("November");
	v_mon.push_back("December");

	for(unsigned int i = 1; i < v_tag->size(); i++)
	{
		string t("000000");	//	time
		for(unsigned int j = 0; j < v_mon.size(); j++)
		{
			if(v_tag->at(0).find(v_mon.at(j)) != v_tag->at(0).npos)
			{
				if(j < 10)
				{
					t.at(1) = '0' + j;
				}
				else
				{
					t.at(0) = '0' + (j%10);
					t.at(1) = '0' + (j/10);
				}
			}
		}

		int sz = v_tag->at(i).size();
		char *s = new char [sz+1];
		for(int j = 0; j < sz; j++)
		{
			s[j] = v_tag->at(i).at(j);
		}
		s[sz] = '\0';

		char *tk, *lc;
		int j;
		for(tk = strtok_s(s, " \n", &lc), j = 0; tk != NULL && j < 6; tk = strtok_s(NULL, " \n", &lc), j++)
		{
			if(strcmp(tk, "\r") == 0)
			{
				j--;
				continue;
			}
			switch(j)
			{
			case 0:	//	日
				if(atoi(tk) < 10)
				{
					t.at(3) = tk[0];
				}
				else
				{
					t.at(2) = tk[0];
					t.at(3) = tk[1];
				}
				break;
			case 1:	//	SRM
				v_srm->at(offset + i-1).addName(tk);
				v_srm->at(offset + i-1).addName(" ");
				break;
			case 2:	//	番号
				v_srm->at(offset + i-1).addName(tk);
				break;
			case 3:	//	時
				if(tk[0] < '0' || '9' < tk[0])
				{
					j--;
					break;
				}
				if(atoi(tk) < 10)
				{
					t.at(5) = tk[0];
				}
				else
				{
					t.at(4) = tk[0];
					t.at(5) = tk[1];
				}
				break;
			case 4:	//	AM / PM
				if(tk[0] == 'P')
				{
					char c[3];
					c[0] = t.at(4);
					c[1] = t.at(5);
					c[2] = '\0';
					int ic = atoi(c);
					t.at(4) = '0' + ((ic+12) / 10);
					t.at(5) = '0' + ((ic+12) % 10);
				}
				break;
			}
		}
		v_srm->at(offset + i-1).setTime(t);

		delete [] s;
	}
}

//---------------------------------------------------------
//	HTML ソースを取得する
//
//	引数
//		url:	HTML ソースの URL
//		src:	HTML ソース
//---------------------------------------------------------
void
getHTML(LPCSTR *p_url, string *p_src)
{
	//
	//	読み込み
	//
	HINTERNET hInternet;
	HINTERNET hFile;
	char *Buf = new char [1001];
	DWORD ReadSize;
	BOOL bResult;

	//	インターネットのハンドルの作成
	hInternet = InternetOpen(
		AGENT_INPUT,
		INTERNET_OPEN_TYPE_PRECONFIG,
		NULL,
		NULL,
		0);

	//	URLのオープン
	hFile = InternetOpenUrl(
		hInternet,
		*p_url,
		NULL,
		0,
		INTERNET_FLAG_RELOAD,
		0);

	while(1)
	{
		//	インターネット上のファイルの読み込み
		bResult = InternetReadFile(
			hFile,
			Buf,
			1000,
			&ReadSize);

		Buf[ReadSize] = '\0';

		string s(Buf);
		*p_src += s;

		//	全て読み込んだらループを抜ける
		if(bResult && ReadSize == 0)
		{
			break;
		}
	}
	InternetCloseHandle(hFile);
	InternetCloseHandle(hInternet);
	delete [] Buf;
}

//---------------------------------------------------------
//	指定のタグとクラス部分を抜き出す
//	引数
//		tag:	指定のタグ
//		clsNm:	指定のクラス
//		src:	HTML ソース
//		pv_tag:	抜き出した string のベクトル
//---------------------------------------------------------
void
getTagArea(string *p_tag, string *p_clsNm, string *p_src, vector<string> *pv_tag)
{
	string startTag("<");		//	開始タグ
	string endTag("</");		//	終了タグ
	string cls("class=\"");		//	クラス
	int  is_inTag = 0;			//	タグの中身か
	int  sTag = 0, eTag = 0;	//	タグの開始と終わり
	int  cTag;					//	クラスの位置
	startTag += *p_tag;
	endTag += *p_tag;
	cls += *p_clsNm;
	cls += "\"";

	string inTag;	//	指定タグの中身
	while(1)
	{
		//	タグ部分を抜き出す
		sTag = p_src->find(startTag, eTag);
		cTag = p_src->find(cls, sTag);
		eTag = p_src->find(endTag, sTag);
		if(sTag == p_src->npos || eTag == p_src->npos)
		{
			break;
		}
		if(sTag < cTag && cTag < eTag)
		{
			string s = p_src->substr(sTag, eTag-sTag+p_tag->size()+3);
			pv_tag->push_back(s);
		}
	}
}

//---------------------------------------------------------
//	指定のタグを消す
//	p_tag:	消したいタグ
//	pv_str:	消したいソース
//---------------------------------------------------------
void
eraseTag(string *p_tag, vector<string> *pv_str)
{
	string startTag("<");		//	開始タグ
	string endTag("</");		//	終了タグ
	startTag += *p_tag;
	endTag += *p_tag;

	for(unsigned int i = 0; i < pv_str->size(); i++)
	{
		int ssTag = 0, seTag = 0;	//	開始タグの開始と終わり
		int esTag = 0, eeTag = 0;	//	終了タグの開始と終わり
		while(1)
		{
			//	タグ部分を抜き出す
			ssTag = pv_str->at(i).find(startTag, seTag);
			seTag = pv_str->at(i).find(">", ssTag);
			if(ssTag == pv_str->at(i).npos || seTag == pv_str->at(i).npos)
			{
				break;
			}
			pv_str->at(i).erase(pv_str->at(i).begin()+ssTag, pv_str->at(i).begin()+seTag+1);

			esTag = pv_str->at(i).find(endTag, eeTag);
			eeTag = pv_str->at(i).find(">", esTag);
			if(esTag == pv_str->at(i).npos || eeTag == pv_str->at(i).npos)
			{
				break;
			}
			pv_str->at(i).erase(pv_str->at(i).begin()+esTag, pv_str->at(i).begin()+eeTag+1);
		}
	}
}



//---------------------------------------------------------
//	今月と来月のカレンダーの URL を作成する
//	引数
//		pv_url:	URL
//	戻り値
//		月 - 1
//---------------------------------------------------------
void
makeURL(vector<LPCSTR> *pv_url)
{
	vector<string> v_mon;	//	月のリスト
	v_mon.push_back("jan_");
	v_mon.push_back("feb_");
	v_mon.push_back("mar_");
	v_mon.push_back("apr_");
	v_mon.push_back("may_");
	v_mon.push_back("jun_");
	v_mon.push_back("jul_");
	v_mon.push_back("aug_");
	v_mon.push_back("sep_");
	v_mon.push_back("oct_");
	v_mon.push_back("nov_");
	v_mon.push_back("dec_");


	for(int i = 0; i < 2; i++)
	{
		string url = "http://www.topcoder.com/tc?module=Static&d1=calendar&d2=";	//	基本

		//	ローカルタイムの取得
		time_t    t, jp_t;
		struct tm jp_tm;
		time(&t);
		localtime_s(&jp_tm, &t);
		jp_t  = mktime(&jp_tm);

		//	月
		int mon = jp_tm.tm_mon;	//	月-1
		url+=v_mon.at(mon+i);

		//	年
		int year = jp_tm.tm_year % 100;
		url += '0' + year/10;
		url += '0' + year%10;

		int sz = url.size();
		char *cURL = new char [sz+1];
		for(int j = 0; j < sz; j++)
		{
			cURL[j] = url.at(j);
		}
		cURL[sz] = '\0';

		pv_url->at(i) = cURL;
	}
}

//---------------------------------------------------------
//	SRM 情報を抜き出す
//	引数
//		pv_srm:	SRM 情報
//---------------------------------------------------------
void
getSRMinfo(vector<srmInfo> *pv_srm)
{
	vector<LPCSTR> v_url(2);
	makeURL(&v_url);

	int offset = 0;
	for(int mon = 0; mon < 2; mon++)
	{
		LPCSTR url = v_url.at(mon);	//	URL
		LPCSTR *p_url = &url;
		
		string src;				//	TopCoder Event Calender の HTML ソース
		vector<string> v_tag;	//	タグごとにまとめたソース
		string tag;
		string cls;

		//	HTMLソースを抜き出す
		getHTML(p_url, &src);

		//	td タグの pageSubtitle クラス部分を抜き出す
		tag = "td";
		cls = "pageSubtitle";
		getTagArea(&tag, &cls, &src, &v_tag);
		//	td タグの value クラス部分を抜き出す
		cls = "value";
		getTagArea(&tag, &cls, &src, &v_tag);

		//	SRM と関係ない要素を削除する
		for(unsigned int i = 1; i < v_tag.size(); i++)
		{
			if(v_tag.at(i).find("class=\"srm\"") == v_tag.at(i).npos)
			{
				v_tag.erase(v_tag.begin()+i);
				i = 0;
			}
		}

		//	タグの削除
		tag = "td";
		eraseTag(&tag, &v_tag);
		tag = "div";
		eraseTag(&tag, &v_tag);
		tag = "strong";
		eraseTag(&tag, &v_tag);
		tag = "a";
		eraseTag(&tag, &v_tag);
		tag = "A";
		eraseTag(&tag, &v_tag);
		tag = "br";
		eraseTag(&tag, &v_tag);

		//	SRM 情報を抜き出す
		pv_srm->resize(pv_srm->size() + v_tag.size() - 1);
		setSRM(&v_tag, pv_srm, offset);

		offset = pv_srm->size();
	}
}

int main()
{
	vector<srmInfo> v_srm;	//	SRM 情報

	//	SRM 情報を抜き出す
	getSRMinfo(&v_srm);

	for(unsigned int i = 0; i < v_srm.size(); i++)
	{
		v_srm.at(i).disp();
		cout << endl;
	}

	cout << "終了するには何かキーを押してください . . ." << endl;
	_getch();

	return 0;
}