NextTrain format file library
NextTrainFile.cpp
- Committer:
- rinosh2
- Date:
- 2010-11-16
- Revision:
- 0:1e951aba6a7e
- Child:
- 1:f89f955130c7
File content as of revision 0:1e951aba6a7e:
/////////////////////////////////////////////////////////////////////////////// // 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 }