NextTrain format file library
NextTrainFile.cpp@2:0aa41f709144, 2010-11-18 (annotated)
- Committer:
- rinosh2
- Date:
- Thu Nov 18 17:40:15 2010 +0000
- Revision:
- 2:0aa41f709144
- Parent:
- 1:f89f955130c7
Add delim char for option
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rinosh2 | 0:1e951aba6a7e | 1 | /////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 2 | // NextTrainFile: NextTrain file parser by rinos 2010 |
rinosh2 | 0:1e951aba6a7e | 3 | /////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 4 | |
rinosh2 | 0:1e951aba6a7e | 5 | #include "NextTrainFile.h" |
rinosh2 | 0:1e951aba6a7e | 6 | #include <time.h> |
rinosh2 | 0:1e951aba6a7e | 7 | #include <stdlib.h> |
rinosh2 | 0:1e951aba6a7e | 8 | |
rinosh2 | 0:1e951aba6a7e | 9 | //////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 10 | // defines |
rinosh2 | 0:1e951aba6a7e | 11 | const int MAX_LINE_BUF = 256; |
rinosh2 | 0:1e951aba6a7e | 12 | |
rinosh2 | 0:1e951aba6a7e | 13 | //////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 14 | // NextTrainFile |
rinosh2 | 0:1e951aba6a7e | 15 | |
rinosh2 | 2:0aa41f709144 | 16 | NextTrainFile::NextTrainFile(const char* ntFile) : m_fp(0), m_shortopt(false), m_delim(' ') { |
rinosh2 | 0:1e951aba6a7e | 17 | if(ntFile) open(ntFile); |
rinosh2 | 0:1e951aba6a7e | 18 | } |
rinosh2 | 0:1e951aba6a7e | 19 | |
rinosh2 | 0:1e951aba6a7e | 20 | NextTrainFile::~NextTrainFile(){ |
rinosh2 | 0:1e951aba6a7e | 21 | close(); |
rinosh2 | 0:1e951aba6a7e | 22 | } |
rinosh2 | 0:1e951aba6a7e | 23 | |
rinosh2 | 0:1e951aba6a7e | 24 | //////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 25 | // internal funcs |
rinosh2 | 0:1e951aba6a7e | 26 | |
rinosh2 | 0:1e951aba6a7e | 27 | int getLineType(char ch){ |
rinosh2 | 0:1e951aba6a7e | 28 | if(ch == '#') return NextTrainFile::LINE_TITLE; |
rinosh2 | 0:1e951aba6a7e | 29 | if(ch < '0') return NextTrainFile::LINE_COMMENT; |
rinosh2 | 0:1e951aba6a7e | 30 | if(ch <= '9') return NextTrainFile::LINE_TIME; |
rinosh2 | 0:1e951aba6a7e | 31 | if(ch < 'A') return NextTrainFile::LINE_COMMENT; |
rinosh2 | 0:1e951aba6a7e | 32 | if(ch <= 'Z') return NextTrainFile::LINE_OPTION; |
rinosh2 | 0:1e951aba6a7e | 33 | if(ch == '[') return NextTrainFile::LINE_WEEK; |
rinosh2 | 0:1e951aba6a7e | 34 | if(ch < 'a') return NextTrainFile::LINE_COMMENT; |
rinosh2 | 0:1e951aba6a7e | 35 | if(ch <= 'z') return NextTrainFile::LINE_OPTION; |
rinosh2 | 0:1e951aba6a7e | 36 | return NextTrainFile::LINE_COMMENT; |
rinosh2 | 0:1e951aba6a7e | 37 | } |
rinosh2 | 2:0aa41f709144 | 38 | |
rinosh2 | 0:1e951aba6a7e | 39 | int getOptionID(char ch){ |
rinosh2 | 0:1e951aba6a7e | 40 | if(ch < 'A') return -1; |
rinosh2 | 0:1e951aba6a7e | 41 | if(ch <= 'Z') return ch - 'A'; |
rinosh2 | 0:1e951aba6a7e | 42 | if(ch < 'a') return -1; |
rinosh2 | 0:1e951aba6a7e | 43 | if(ch <= 'z') return ch - 'a' + 26; |
rinosh2 | 0:1e951aba6a7e | 44 | return -1; |
rinosh2 | 0:1e951aba6a7e | 45 | } |
rinosh2 | 0:1e951aba6a7e | 46 | |
rinosh2 | 2:0aa41f709144 | 47 | const char* WEEK_LIST[] = { // week + holiday |
rinosh2 | 2:0aa41f709144 | 48 | "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT", "HOL" |
rinosh2 | 2:0aa41f709144 | 49 | }; |
rinosh2 | 0:1e951aba6a7e | 50 | int getWeekMask(char* str){ |
rinosh2 | 0:1e951aba6a7e | 51 | const char WEEK_DELIM[] = " []\t\r\n"; |
rinosh2 | 0:1e951aba6a7e | 52 | |
rinosh2 | 0:1e951aba6a7e | 53 | int ret = 0; |
rinosh2 | 0:1e951aba6a7e | 54 | char * p = strtok(str, WEEK_DELIM); |
rinosh2 | 0:1e951aba6a7e | 55 | while(p){ |
rinosh2 | 0:1e951aba6a7e | 56 | for(int index = 0; index < 8 ; ++index){ // week7 + holiday |
rinosh2 | 2:0aa41f709144 | 57 | if(strcmp(p, WEEK_LIST[index]) == 0){ // stricmp? |
rinosh2 | 0:1e951aba6a7e | 58 | ret |= 1 << index; |
rinosh2 | 0:1e951aba6a7e | 59 | break; |
rinosh2 | 0:1e951aba6a7e | 60 | } |
rinosh2 | 0:1e951aba6a7e | 61 | } |
rinosh2 | 0:1e951aba6a7e | 62 | p = strtok(0, WEEK_DELIM); |
rinosh2 | 0:1e951aba6a7e | 63 | } |
rinosh2 | 0:1e951aba6a7e | 64 | return ret; |
rinosh2 | 1:f89f955130c7 | 65 | } |
rinosh2 | 0:1e951aba6a7e | 66 | |
rinosh2 | 0:1e951aba6a7e | 67 | //////////////////////////////////////////////////////////////////////////////// |
rinosh2 | 0:1e951aba6a7e | 68 | // Open NextTrain format file and read option data |
rinosh2 | 0:1e951aba6a7e | 69 | NextTrainFile::Status NextTrainFile::open(const char* ntFile){ |
rinosh2 | 0:1e951aba6a7e | 70 | Status ret = S_SUCCESS; |
rinosh2 | 0:1e951aba6a7e | 71 | |
rinosh2 | 0:1e951aba6a7e | 72 | // open check |
rinosh2 | 0:1e951aba6a7e | 73 | if(m_fp) return S_ALREADY_OPEN; |
rinosh2 | 0:1e951aba6a7e | 74 | m_fp = fopen(ntFile, "r"); |
rinosh2 | 0:1e951aba6a7e | 75 | if(!m_fp) return S_OPEN_ERROR; |
rinosh2 | 0:1e951aba6a7e | 76 | |
rinosh2 | 0:1e951aba6a7e | 77 | // Read option info |
rinosh2 | 0:1e951aba6a7e | 78 | memset(m_index, 0xff, sizeof(m_index)); |
rinosh2 | 0:1e951aba6a7e | 79 | int pos = 0; |
rinosh2 | 0:1e951aba6a7e | 80 | int left = MAX_OPTBUF - 1; |
rinosh2 | 0:1e951aba6a7e | 81 | char line[MAX_LINE_BUF]; |
rinosh2 | 0:1e951aba6a7e | 82 | while(fgets(line, sizeof(line), m_fp)){ |
rinosh2 | 0:1e951aba6a7e | 83 | switch(getLineType(*line)){ |
rinosh2 | 0:1e951aba6a7e | 84 | case LINE_TITLE: |
rinosh2 | 0:1e951aba6a7e | 85 | case LINE_TIME: |
rinosh2 | 0:1e951aba6a7e | 86 | case LINE_WEEK: |
rinosh2 | 0:1e951aba6a7e | 87 | // Don't cache (for saving the memory) |
rinosh2 | 0:1e951aba6a7e | 88 | break; |
rinosh2 | 0:1e951aba6a7e | 89 | |
rinosh2 | 0:1e951aba6a7e | 90 | case LINE_OPTION: |
rinosh2 | 0:1e951aba6a7e | 91 | { |
rinosh2 | 0:1e951aba6a7e | 92 | // Decode option / e.g. "a:Express;EX" |
rinosh2 | 0:1e951aba6a7e | 93 | int optID = getOptionID(line[0]); // 0..51 |
rinosh2 | 0:1e951aba6a7e | 94 | if(optID < 0 || line[1] != ':') continue; // assert? |
rinosh2 | 0:1e951aba6a7e | 95 | |
rinosh2 | 0:1e951aba6a7e | 96 | const char OPTION_DELIM[] = " \t\r\n"; |
rinosh2 | 0:1e951aba6a7e | 97 | char* p = strtok(line + 2, OPTION_DELIM); |
rinosh2 | 0:1e951aba6a7e | 98 | if(!p) continue; // skip empty option |
rinosh2 | 0:1e951aba6a7e | 99 | |
rinosh2 | 0:1e951aba6a7e | 100 | int len = strlen(p) + 1; |
rinosh2 | 0:1e951aba6a7e | 101 | if(len > left){ |
rinosh2 | 0:1e951aba6a7e | 102 | ret = S_OPTION_OVERFLOW; // MAX_OPTBUF is too small... |
rinosh2 | 0:1e951aba6a7e | 103 | continue; |
rinosh2 | 0:1e951aba6a7e | 104 | } |
rinosh2 | 0:1e951aba6a7e | 105 | m_index[optID] = (unsigned char)pos; |
rinosh2 | 0:1e951aba6a7e | 106 | memcpy(m_optbuf + pos, p, len); |
rinosh2 | 0:1e951aba6a7e | 107 | pos += len; |
rinosh2 | 0:1e951aba6a7e | 108 | left -= len; |
rinosh2 | 0:1e951aba6a7e | 109 | } |
rinosh2 | 0:1e951aba6a7e | 110 | break; |
rinosh2 | 0:1e951aba6a7e | 111 | } |
rinosh2 | 0:1e951aba6a7e | 112 | } |
rinosh2 | 2:0aa41f709144 | 113 | printf("NextTrainFile::open return %d, read %d bytes\n", ret, pos); |
rinosh2 | 0:1e951aba6a7e | 114 | return ret; |
rinosh2 | 0:1e951aba6a7e | 115 | } |
rinosh2 | 2:0aa41f709144 | 116 | |
rinosh2 | 0:1e951aba6a7e | 117 | NextTrainFile::Status NextTrainFile::close(){ |
rinosh2 | 0:1e951aba6a7e | 118 | if(m_fp){ |
rinosh2 | 0:1e951aba6a7e | 119 | fclose(m_fp); // no error check... |
rinosh2 | 0:1e951aba6a7e | 120 | m_fp = 0; |
rinosh2 | 0:1e951aba6a7e | 121 | } |
rinosh2 | 0:1e951aba6a7e | 122 | return S_SUCCESS; |
rinosh2 | 0:1e951aba6a7e | 123 | } |
rinosh2 | 0:1e951aba6a7e | 124 | |
rinosh2 | 0:1e951aba6a7e | 125 | NextTrainFile::Status NextTrainFile::search(time_t dt, int offset){ |
rinosh2 | 0:1e951aba6a7e | 126 | if(!m_fp) return S_OPEN_ERROR; // Closed |
rinosh2 | 0:1e951aba6a7e | 127 | |
rinosh2 | 0:1e951aba6a7e | 128 | // Set target time/week |
rinosh2 | 0:1e951aba6a7e | 129 | if(!dt) dt = time(0); // get current time |
rinosh2 | 0:1e951aba6a7e | 130 | struct tm* st = localtime(&dt); |
rinosh2 | 0:1e951aba6a7e | 131 | int week_flag = 1 << st->tm_wday; // TODO HOLIDAY! |
rinosh2 | 0:1e951aba6a7e | 132 | unsigned int target = st->tm_hour; |
rinosh2 | 0:1e951aba6a7e | 133 | if(target < HOUR_DIV) target += 24; |
rinosh2 | 0:1e951aba6a7e | 134 | target = target * 60 + st->tm_min; |
rinosh2 | 0:1e951aba6a7e | 135 | |
rinosh2 | 0:1e951aba6a7e | 136 | // Scan from head of file |
rinosh2 | 0:1e951aba6a7e | 137 | rewind(m_fp); |
rinosh2 | 0:1e951aba6a7e | 138 | int week_mask = -1; // default: all |
rinosh2 | 0:1e951aba6a7e | 139 | char line[MAX_LINE_BUF]; |
rinosh2 | 0:1e951aba6a7e | 140 | const char TIME_LINE_DELIM[] = " \t\r\n"; |
rinosh2 | 0:1e951aba6a7e | 141 | |
rinosh2 | 0:1e951aba6a7e | 142 | while(fgets(line, sizeof(line), m_fp)){ |
rinosh2 | 0:1e951aba6a7e | 143 | switch(getLineType(*line)){ |
rinosh2 | 0:1e951aba6a7e | 144 | case LINE_TITLE: |
rinosh2 | 0:1e951aba6a7e | 145 | if(week_mask & week_flag) { |
rinosh2 | 0:1e951aba6a7e | 146 | char* p1 = line + 1; |
rinosh2 | 0:1e951aba6a7e | 147 | while(*p1 && strchr(TIME_LINE_DELIM, *p1)) ++p1; |
rinosh2 | 0:1e951aba6a7e | 148 | char* p2 = p1 + strlen(p1); |
rinosh2 | 0:1e951aba6a7e | 149 | while(p1 < p2 && strchr(TIME_LINE_DELIM, *p2)) --p2; |
rinosh2 | 0:1e951aba6a7e | 150 | int len = p2 - p1; |
rinosh2 | 0:1e951aba6a7e | 151 | if(*p1 && ++len > sizeof(m_ni.m_title) - 1){ |
rinosh2 | 0:1e951aba6a7e | 152 | len = sizeof(m_ni.m_title) - 1; |
rinosh2 | 0:1e951aba6a7e | 153 | } |
rinosh2 | 0:1e951aba6a7e | 154 | memcpy(m_ni.m_title, p1, len); |
rinosh2 | 0:1e951aba6a7e | 155 | m_ni.m_title[len] = 0; |
rinosh2 | 0:1e951aba6a7e | 156 | } |
rinosh2 | 0:1e951aba6a7e | 157 | break; |
rinosh2 | 0:1e951aba6a7e | 158 | |
rinosh2 | 0:1e951aba6a7e | 159 | case LINE_TIME: |
rinosh2 | 0:1e951aba6a7e | 160 | // Decode option / e.g. "5: ic14 ie19 hc33 ie39 ie54" |
rinosh2 | 0:1e951aba6a7e | 161 | if(week_mask & week_flag){ |
rinosh2 | 0:1e951aba6a7e | 162 | // hour format check |
rinosh2 | 0:1e951aba6a7e | 163 | char* p = strtok(line, ":"); |
rinosh2 | 0:1e951aba6a7e | 164 | if(!p) break; // Invalid hour line! (return error?) |
rinosh2 | 0:1e951aba6a7e | 165 | int hour = atoi(p); |
rinosh2 | 0:1e951aba6a7e | 166 | if(hour > 23 + HOUR_DIV) break; // Invalid time! (return error?) |
rinosh2 | 0:1e951aba6a7e | 167 | |
rinosh2 | 0:1e951aba6a7e | 168 | // check hour |
rinosh2 | 0:1e951aba6a7e | 169 | m_ni.m_hour = hour; |
rinosh2 | 0:1e951aba6a7e | 170 | if(hour < HOUR_DIV) hour += 24; |
rinosh2 | 0:1e951aba6a7e | 171 | unsigned int hm = hour * 60; |
rinosh2 | 0:1e951aba6a7e | 172 | if(hm + 60 < target) break; // passed |
rinosh2 | 0:1e951aba6a7e | 173 | |
rinosh2 | 0:1e951aba6a7e | 174 | // search target min |
rinosh2 | 0:1e951aba6a7e | 175 | while((p = strtok(0, TIME_LINE_DELIM)) != 0){ |
rinosh2 | 0:1e951aba6a7e | 176 | // min format check |
rinosh2 | 0:1e951aba6a7e | 177 | char* opt = p; |
rinosh2 | 0:1e951aba6a7e | 178 | while(*p && !strchr("0123456789", *p)) ++p; |
rinosh2 | 0:1e951aba6a7e | 179 | if(!*p) continue; // Invalid min format! (return error?) |
rinosh2 | 0:1e951aba6a7e | 180 | m_ni.m_min = atoi(p); |
rinosh2 | 0:1e951aba6a7e | 181 | if(m_ni.m_min >= 60) continue; // Invalid min format! (return error?) |
rinosh2 | 0:1e951aba6a7e | 182 | |
rinosh2 | 0:1e951aba6a7e | 183 | // check min |
rinosh2 | 0:1e951aba6a7e | 184 | if(hm + m_ni.m_min <= target) continue; // passed |
rinosh2 | 0:1e951aba6a7e | 185 | |
rinosh2 | 0:1e951aba6a7e | 186 | if(offset-- > 0) continue; // view next |
rinosh2 | 0:1e951aba6a7e | 187 | |
rinosh2 | 0:1e951aba6a7e | 188 | // Found NextTrain |
rinosh2 | 0:1e951aba6a7e | 189 | //m_ni.m_hour = hour; // use 24:00 instead of 00:00 |
rinosh2 | 0:1e951aba6a7e | 190 | m_ni.m_diff = (hm + m_ni.m_min - target) * 60 - st->tm_sec; // leap seconds (max 61)? |
rinosh2 | 0:1e951aba6a7e | 191 | char* dst = m_ni.m_option; |
rinosh2 | 0:1e951aba6a7e | 192 | int left = MAX_OPTION - 1; |
rinosh2 | 0:1e951aba6a7e | 193 | for(; opt < p ; ++opt){ |
rinosh2 | 0:1e951aba6a7e | 194 | // check option format |
rinosh2 | 0:1e951aba6a7e | 195 | int optID = getOptionID(*opt); |
rinosh2 | 2:0aa41f709144 | 196 | if(optID < 0 || m_index[optID] == 0xff){ |
rinosh2 | 2:0aa41f709144 | 197 | printf("NextTrainFile::search S_INVALID_OPTION_ID %x\n", (optID < 0)? -1 : m_index[optID]); |
rinosh2 | 2:0aa41f709144 | 198 | return S_INVALID_OPTION_ID; |
rinosh2 | 2:0aa41f709144 | 199 | } |
rinosh2 | 0:1e951aba6a7e | 200 | |
rinosh2 | 0:1e951aba6a7e | 201 | // append option string |
rinosh2 | 0:1e951aba6a7e | 202 | char* src = m_optbuf + m_index[optID]; |
rinosh2 | 0:1e951aba6a7e | 203 | char* sep = strchr(src, ';'); |
rinosh2 | 0:1e951aba6a7e | 204 | int len; |
rinosh2 | 0:1e951aba6a7e | 205 | if(sep){ |
rinosh2 | 0:1e951aba6a7e | 206 | if(m_shortopt){ |
rinosh2 | 0:1e951aba6a7e | 207 | // abbreviation |
rinosh2 | 0:1e951aba6a7e | 208 | src = sep + 1; |
rinosh2 | 0:1e951aba6a7e | 209 | len = strlen(src); |
rinosh2 | 0:1e951aba6a7e | 210 | } else { |
rinosh2 | 0:1e951aba6a7e | 211 | // long description |
rinosh2 | 0:1e951aba6a7e | 212 | len = sep - src; |
rinosh2 | 0:1e951aba6a7e | 213 | } |
rinosh2 | 0:1e951aba6a7e | 214 | } else { |
rinosh2 | 0:1e951aba6a7e | 215 | // no separator |
rinosh2 | 0:1e951aba6a7e | 216 | len = strlen(src); |
rinosh2 | 0:1e951aba6a7e | 217 | } |
rinosh2 | 0:1e951aba6a7e | 218 | |
rinosh2 | 2:0aa41f709144 | 219 | if(len + 1 > left){ |
rinosh2 | 2:0aa41f709144 | 220 | printf("NextTrainFile::search S_OPTION_OVERFLOW\n"); |
rinosh2 | 2:0aa41f709144 | 221 | return S_OPTION_OVERFLOW; // MAX_OPTION is too small... |
rinosh2 | 2:0aa41f709144 | 222 | } |
rinosh2 | 2:0aa41f709144 | 223 | if(dst != m_ni.m_option && m_delim){ |
rinosh2 | 2:0aa41f709144 | 224 | *dst++ = m_delim; |
rinosh2 | 2:0aa41f709144 | 225 | left--; |
rinosh2 | 2:0aa41f709144 | 226 | } |
rinosh2 | 0:1e951aba6a7e | 227 | memcpy(dst, src, len); |
rinosh2 | 0:1e951aba6a7e | 228 | dst += len; |
rinosh2 | 0:1e951aba6a7e | 229 | left -= len; |
rinosh2 | 0:1e951aba6a7e | 230 | } |
rinosh2 | 0:1e951aba6a7e | 231 | *dst = 0; |
rinosh2 | 2:0aa41f709144 | 232 | printf("NextTrainFile::search %02d:%02d:%02d(%s) hit train %d:%02d\n", st->tm_hour, st->tm_min, st->tm_sec, WEEK_LIST[st->tm_wday], m_ni.m_hour, m_ni.m_min); |
rinosh2 | 0:1e951aba6a7e | 233 | return S_SUCCESS; |
rinosh2 | 0:1e951aba6a7e | 234 | } |
rinosh2 | 0:1e951aba6a7e | 235 | } |
rinosh2 | 0:1e951aba6a7e | 236 | break; |
rinosh2 | 0:1e951aba6a7e | 237 | |
rinosh2 | 0:1e951aba6a7e | 238 | case LINE_WEEK: |
rinosh2 | 0:1e951aba6a7e | 239 | week_mask = getWeekMask(line); |
rinosh2 | 0:1e951aba6a7e | 240 | break; |
rinosh2 | 0:1e951aba6a7e | 241 | |
rinosh2 | 0:1e951aba6a7e | 242 | case LINE_OPTION: |
rinosh2 | 0:1e951aba6a7e | 243 | // skip (use cache) |
rinosh2 | 0:1e951aba6a7e | 244 | break; |
rinosh2 | 0:1e951aba6a7e | 245 | } |
rinosh2 | 0:1e951aba6a7e | 246 | } |
rinosh2 | 0:1e951aba6a7e | 247 | |
rinosh2 | 2:0aa41f709144 | 248 | printf("NextTrainFile::search %02d:%02d:%02d(%s) NoTrain\n", st->tm_hour, st->tm_min, st->tm_sec, WEEK_LIST[st->tm_wday]); |
rinosh2 | 2:0aa41f709144 | 249 | |
rinosh2 | 0:1e951aba6a7e | 250 | return S_NO_TRAIN; // failed |
rinosh2 | 0:1e951aba6a7e | 251 | } |
rinosh2 | 0:1e951aba6a7e | 252 |