NextTrain format file library

Revision:
0:1e951aba6a7e
Child:
1:f89f955130c7
diff -r 000000000000 -r 1e951aba6a7e NextTrainFile.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NextTrainFile.cpp	Tue Nov 16 16:38:02 2010 +0000
@@ -0,0 +1,238 @@
+///////////////////////////////////////////////////////////////////////////////
+// NextTrainFile: NextTrain file parser   by rinos 2010
+///////////////////////////////////////////////////////////////////////////////
+
+#include "NextTrainFile.h"
+#include <time.h>
+#include <stdlib.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// defines
+const int MAX_LINE_BUF = 256;
+
+////////////////////////////////////////////////////////////////////////////////
+// NextTrainFile
+
+NextTrainFile::NextTrainFile(const char* ntFile) : m_fp(0), m_shortopt(false) {
+	if(ntFile) open(ntFile);
+}
+
+NextTrainFile::~NextTrainFile(){
+	close();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// internal funcs
+
+int getLineType(char ch){
+	if(ch == '#')	return NextTrainFile::LINE_TITLE;
+	if(ch <  '0')	return NextTrainFile::LINE_COMMENT;
+	if(ch <= '9')	return NextTrainFile::LINE_TIME;
+	if(ch <  'A')	return NextTrainFile::LINE_COMMENT;
+	if(ch <= 'Z')	return NextTrainFile::LINE_OPTION;
+	if(ch == '[')	return NextTrainFile::LINE_WEEK;
+	if(ch <  'a')	return NextTrainFile::LINE_COMMENT;
+	if(ch <= 'z')	return NextTrainFile::LINE_OPTION;
+	return NextTrainFile::LINE_COMMENT;
+}
+int getOptionID(char ch){
+	if(ch <  'A')	return -1;
+	if(ch <= 'Z')	return ch - 'A';
+	if(ch <  'a')	return -1;
+	if(ch <= 'z')	return ch - 'a' + 26;
+	return -1;
+}
+
+int getWeekMask(char* str){
+	const char WEEK_DELIM[] = " []\t\r\n";
+	const char* WEEK_LIST[] = { // week + holiday
+		"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", "HOL"
+	};
+	
+	int ret = 0;
+	char * p = strtok(str, WEEK_DELIM);
+	while(p){
+		for(int index = 0; index < 8 ; ++index){ // week7 + holiday
+//			if(stricmp(p, WEEK_LIST[index]) == 0){
+			if(strcmp(p, WEEK_LIST[index]) == 0){
+				ret |= 1 << index;
+				break;
+			}
+		}
+		p = strtok(0, WEEK_DELIM);
+	}
+	return ret;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Open NextTrain format file and read option data
+NextTrainFile::Status NextTrainFile::open(const char* ntFile){
+	Status ret = S_SUCCESS;
+
+	// open check
+	if(m_fp) return S_ALREADY_OPEN;
+	m_fp = fopen(ntFile, "r");
+	if(!m_fp) return S_OPEN_ERROR;
+	
+	// Read option info
+	memset(m_index, 0xff, sizeof(m_index));
+	int pos = 0;
+	int left = MAX_OPTBUF - 1;
+	char line[MAX_LINE_BUF];
+	while(fgets(line, sizeof(line), m_fp)){
+		switch(getLineType(*line)){
+		case LINE_TITLE:
+		case LINE_TIME:
+		case LINE_WEEK:
+			// Don't cache (for saving the memory)
+			break;
+
+		case LINE_OPTION:
+			{
+				// Decode option / e.g. "a:Express;EX"
+				int optID = getOptionID(line[0]); // 0..51
+				if(optID < 0 || line[1] != ':') continue; // assert?
+
+				const char OPTION_DELIM[] = " \t\r\n";
+				char* p = strtok(line + 2, OPTION_DELIM);
+				if(!p) continue; // skip empty option
+
+				int len = strlen(p) + 1;
+				if(len > left){
+					ret = S_OPTION_OVERFLOW; // MAX_OPTBUF is too small...
+					continue;
+				}
+				m_index[optID] = (unsigned char)pos;
+				memcpy(m_optbuf + pos, p, len);
+				pos  += len;
+				left -= len;
+			}
+			break;
+		}
+	}
+	return ret;
+}
+NextTrainFile::Status NextTrainFile::close(){
+	if(m_fp){
+		fclose(m_fp); // no error check...
+		m_fp = 0;
+	}
+	return S_SUCCESS;
+}
+
+NextTrainFile::Status NextTrainFile::search(time_t dt, int offset){
+	if(!m_fp) return S_OPEN_ERROR; // Closed
+
+	// Set target time/week
+	if(!dt) dt = time(0); // get current time
+	struct tm* st = localtime(&dt);
+	int week_flag = 1 << st->tm_wday; // TODO HOLIDAY!
+	unsigned int target = st->tm_hour;
+	if(target < HOUR_DIV) target += 24;
+	target = target * 60 + st->tm_min;
+
+	// Scan from head of file
+	rewind(m_fp);
+	int week_mask = -1; // default: all
+	char line[MAX_LINE_BUF];
+	const char TIME_LINE_DELIM[] = " \t\r\n";
+
+	while(fgets(line, sizeof(line), m_fp)){
+		switch(getLineType(*line)){
+		case LINE_TITLE:
+			if(week_mask & week_flag) {
+				char* p1 = line + 1;
+				while(*p1     && strchr(TIME_LINE_DELIM, *p1)) ++p1;
+				char* p2 = p1 + strlen(p1);
+				while(p1 < p2 && strchr(TIME_LINE_DELIM, *p2)) --p2;
+				int len = p2 - p1;
+				if(*p1 && ++len > sizeof(m_ni.m_title) - 1){
+					len = sizeof(m_ni.m_title) - 1;
+				}
+				memcpy(m_ni.m_title, p1, len);
+				m_ni.m_title[len] = 0;
+			}
+			break;
+
+		case LINE_TIME:
+			// Decode option / e.g. "5: ic14 ie19 hc33 ie39 ie54"
+			if(week_mask & week_flag){
+				// hour format check
+				char* p = strtok(line, ":");
+				if(!p) break; // Invalid hour line! (return error?)
+				int hour = atoi(p);
+				if(hour > 23 + HOUR_DIV) break; // Invalid time! (return error?)
+
+				// check hour
+				m_ni.m_hour = hour;
+				if(hour < HOUR_DIV) hour += 24;
+				unsigned int hm = hour * 60;
+				if(hm + 60 < target) break; // passed
+
+				// search target min
+				while((p = strtok(0, TIME_LINE_DELIM)) != 0){
+					// min format check
+					char* opt = p;
+					while(*p && !strchr("0123456789", *p)) ++p;
+					if(!*p) continue; // Invalid min format! (return error?)
+					m_ni.m_min = atoi(p);
+					if(m_ni.m_min >= 60) continue; // Invalid min format! (return error?)
+
+					// check min
+					if(hm + m_ni.m_min <= target) continue; // passed
+
+					if(offset-- > 0) continue; // view next
+					
+					// Found NextTrain
+					//m_ni.m_hour = hour; // use 24:00 instead of 00:00
+					m_ni.m_diff = (hm + m_ni.m_min - target) * 60 - st->tm_sec; // leap seconds (max 61)?
+					char* dst  = m_ni.m_option;
+					int   left = MAX_OPTION - 1;
+					for(; opt < p ; ++opt){
+						// check option format
+						int optID = getOptionID(*opt);
+						if(optID < 0 || m_index[optID] == 0xff) return S_INVALID_OPTION_ID;
+
+						// append option string
+						char* src = m_optbuf + m_index[optID];
+						char* sep = strchr(src, ';');
+						int   len;
+						if(sep){
+							if(m_shortopt){
+								// abbreviation
+								src = sep + 1;
+								len = strlen(src);
+							} else {
+								// long description
+								len = sep - src;
+							}
+						} else {
+							// no separator
+							len = strlen(src);
+						}
+
+						if(len > left) return S_OPTION_OVERFLOW; // MAX_OPTION is too small...
+						memcpy(dst, src, len);
+						dst  += len;
+						left -= len;
+					}
+					*dst = 0;
+					return S_SUCCESS;
+				}
+			}
+			break;
+
+		case LINE_WEEK:
+			week_mask = getWeekMask(line);
+			break;
+
+		case LINE_OPTION:
+			// skip (use cache)
+			break;
+		}
+	}
+
+	return S_NO_TRAIN; // failed
+}
+
+