NextTrain format file library
Diff: NextTrainFile.cpp
- Revision:
- 0:1e951aba6a7e
- Child:
- 1:f89f955130c7
--- /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 +} + +