NextTrain format file library
NextTrainFile.cpp@0:1e951aba6a7e, 2010-11-16 (annotated)
- Committer:
- rinosh2
- Date:
- Tue Nov 16 16:38:02 2010 +0000
- Revision:
- 0:1e951aba6a7e
- Child:
- 1:f89f955130c7
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 | 0:1e951aba6a7e | 16 | NextTrainFile::NextTrainFile(const char* ntFile) : m_fp(0), m_shortopt(false) { |
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 | 0:1e951aba6a7e | 38 | int getOptionID(char ch){ |
rinosh2 | 0:1e951aba6a7e | 39 | if(ch < 'A') return -1; |
rinosh2 | 0:1e951aba6a7e | 40 | if(ch <= 'Z') return ch - 'A'; |
rinosh2 | 0:1e951aba6a7e | 41 | if(ch < 'a') return -1; |
rinosh2 | 0:1e951aba6a7e | 42 | if(ch <= 'z') return ch - 'a' + 26; |
rinosh2 | 0:1e951aba6a7e | 43 | return -1; |
rinosh2 | 0:1e951aba6a7e | 44 | } |
rinosh2 | 0:1e951aba6a7e | 45 | |
rinosh2 | 0:1e951aba6a7e | 46 | int getWeekMask(char* str){ |
rinosh2 | 0:1e951aba6a7e | 47 | const char WEEK_DELIM[] = " []\t\r\n"; |
rinosh2 | 0:1e951aba6a7e | 48 | const char* WEEK_LIST[] = { // week + holiday |
rinosh2 | 0:1e951aba6a7e | 49 | "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN", "HOL" |
rinosh2 | 0:1e951aba6a7e | 50 | }; |
rinosh2 | 0:1e951aba6a7e | 51 | |
rinosh2 | 0:1e951aba6a7e | 52 | int ret = 0; |
rinosh2 | 0:1e951aba6a7e | 53 | char * p = strtok(str, WEEK_DELIM); |
rinosh2 | 0:1e951aba6a7e | 54 | while(p){ |
rinosh2 | 0:1e951aba6a7e | 55 | for(int index = 0; index < 8 ; ++index){ // week7 + holiday |
rinosh2 | 0:1e951aba6a7e | 56 | // if(stricmp(p, WEEK_LIST[index]) == 0){ |
rinosh2 | 0:1e951aba6a7e | 57 | if(strcmp(p, WEEK_LIST[index]) == 0){ |
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 | 0:1e951aba6a7e | 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 | 0:1e951aba6a7e | 113 | return ret; |
rinosh2 | 0:1e951aba6a7e | 114 | } |
rinosh2 | 0:1e951aba6a7e | 115 | NextTrainFile::Status NextTrainFile::close(){ |
rinosh2 | 0:1e951aba6a7e | 116 | if(m_fp){ |
rinosh2 | 0:1e951aba6a7e | 117 | fclose(m_fp); // no error check... |
rinosh2 | 0:1e951aba6a7e | 118 | m_fp = 0; |
rinosh2 | 0:1e951aba6a7e | 119 | } |
rinosh2 | 0:1e951aba6a7e | 120 | return S_SUCCESS; |
rinosh2 | 0:1e951aba6a7e | 121 | } |
rinosh2 | 0:1e951aba6a7e | 122 | |
rinosh2 | 0:1e951aba6a7e | 123 | NextTrainFile::Status NextTrainFile::search(time_t dt, int offset){ |
rinosh2 | 0:1e951aba6a7e | 124 | if(!m_fp) return S_OPEN_ERROR; // Closed |
rinosh2 | 0:1e951aba6a7e | 125 | |
rinosh2 | 0:1e951aba6a7e | 126 | // Set target time/week |
rinosh2 | 0:1e951aba6a7e | 127 | if(!dt) dt = time(0); // get current time |
rinosh2 | 0:1e951aba6a7e | 128 | struct tm* st = localtime(&dt); |
rinosh2 | 0:1e951aba6a7e | 129 | int week_flag = 1 << st->tm_wday; // TODO HOLIDAY! |
rinosh2 | 0:1e951aba6a7e | 130 | unsigned int target = st->tm_hour; |
rinosh2 | 0:1e951aba6a7e | 131 | if(target < HOUR_DIV) target += 24; |
rinosh2 | 0:1e951aba6a7e | 132 | target = target * 60 + st->tm_min; |
rinosh2 | 0:1e951aba6a7e | 133 | |
rinosh2 | 0:1e951aba6a7e | 134 | // Scan from head of file |
rinosh2 | 0:1e951aba6a7e | 135 | rewind(m_fp); |
rinosh2 | 0:1e951aba6a7e | 136 | int week_mask = -1; // default: all |
rinosh2 | 0:1e951aba6a7e | 137 | char line[MAX_LINE_BUF]; |
rinosh2 | 0:1e951aba6a7e | 138 | const char TIME_LINE_DELIM[] = " \t\r\n"; |
rinosh2 | 0:1e951aba6a7e | 139 | |
rinosh2 | 0:1e951aba6a7e | 140 | while(fgets(line, sizeof(line), m_fp)){ |
rinosh2 | 0:1e951aba6a7e | 141 | switch(getLineType(*line)){ |
rinosh2 | 0:1e951aba6a7e | 142 | case LINE_TITLE: |
rinosh2 | 0:1e951aba6a7e | 143 | if(week_mask & week_flag) { |
rinosh2 | 0:1e951aba6a7e | 144 | char* p1 = line + 1; |
rinosh2 | 0:1e951aba6a7e | 145 | while(*p1 && strchr(TIME_LINE_DELIM, *p1)) ++p1; |
rinosh2 | 0:1e951aba6a7e | 146 | char* p2 = p1 + strlen(p1); |
rinosh2 | 0:1e951aba6a7e | 147 | while(p1 < p2 && strchr(TIME_LINE_DELIM, *p2)) --p2; |
rinosh2 | 0:1e951aba6a7e | 148 | int len = p2 - p1; |
rinosh2 | 0:1e951aba6a7e | 149 | if(*p1 && ++len > sizeof(m_ni.m_title) - 1){ |
rinosh2 | 0:1e951aba6a7e | 150 | len = sizeof(m_ni.m_title) - 1; |
rinosh2 | 0:1e951aba6a7e | 151 | } |
rinosh2 | 0:1e951aba6a7e | 152 | memcpy(m_ni.m_title, p1, len); |
rinosh2 | 0:1e951aba6a7e | 153 | m_ni.m_title[len] = 0; |
rinosh2 | 0:1e951aba6a7e | 154 | } |
rinosh2 | 0:1e951aba6a7e | 155 | break; |
rinosh2 | 0:1e951aba6a7e | 156 | |
rinosh2 | 0:1e951aba6a7e | 157 | case LINE_TIME: |
rinosh2 | 0:1e951aba6a7e | 158 | // Decode option / e.g. "5: ic14 ie19 hc33 ie39 ie54" |
rinosh2 | 0:1e951aba6a7e | 159 | if(week_mask & week_flag){ |
rinosh2 | 0:1e951aba6a7e | 160 | // hour format check |
rinosh2 | 0:1e951aba6a7e | 161 | char* p = strtok(line, ":"); |
rinosh2 | 0:1e951aba6a7e | 162 | if(!p) break; // Invalid hour line! (return error?) |
rinosh2 | 0:1e951aba6a7e | 163 | int hour = atoi(p); |
rinosh2 | 0:1e951aba6a7e | 164 | if(hour > 23 + HOUR_DIV) break; // Invalid time! (return error?) |
rinosh2 | 0:1e951aba6a7e | 165 | |
rinosh2 | 0:1e951aba6a7e | 166 | // check hour |
rinosh2 | 0:1e951aba6a7e | 167 | m_ni.m_hour = hour; |
rinosh2 | 0:1e951aba6a7e | 168 | if(hour < HOUR_DIV) hour += 24; |
rinosh2 | 0:1e951aba6a7e | 169 | unsigned int hm = hour * 60; |
rinosh2 | 0:1e951aba6a7e | 170 | if(hm + 60 < target) break; // passed |
rinosh2 | 0:1e951aba6a7e | 171 | |
rinosh2 | 0:1e951aba6a7e | 172 | // search target min |
rinosh2 | 0:1e951aba6a7e | 173 | while((p = strtok(0, TIME_LINE_DELIM)) != 0){ |
rinosh2 | 0:1e951aba6a7e | 174 | // min format check |
rinosh2 | 0:1e951aba6a7e | 175 | char* opt = p; |
rinosh2 | 0:1e951aba6a7e | 176 | while(*p && !strchr("0123456789", *p)) ++p; |
rinosh2 | 0:1e951aba6a7e | 177 | if(!*p) continue; // Invalid min format! (return error?) |
rinosh2 | 0:1e951aba6a7e | 178 | m_ni.m_min = atoi(p); |
rinosh2 | 0:1e951aba6a7e | 179 | if(m_ni.m_min >= 60) continue; // Invalid min format! (return error?) |
rinosh2 | 0:1e951aba6a7e | 180 | |
rinosh2 | 0:1e951aba6a7e | 181 | // check min |
rinosh2 | 0:1e951aba6a7e | 182 | if(hm + m_ni.m_min <= target) continue; // passed |
rinosh2 | 0:1e951aba6a7e | 183 | |
rinosh2 | 0:1e951aba6a7e | 184 | if(offset-- > 0) continue; // view next |
rinosh2 | 0:1e951aba6a7e | 185 | |
rinosh2 | 0:1e951aba6a7e | 186 | // Found NextTrain |
rinosh2 | 0:1e951aba6a7e | 187 | //m_ni.m_hour = hour; // use 24:00 instead of 00:00 |
rinosh2 | 0:1e951aba6a7e | 188 | m_ni.m_diff = (hm + m_ni.m_min - target) * 60 - st->tm_sec; // leap seconds (max 61)? |
rinosh2 | 0:1e951aba6a7e | 189 | char* dst = m_ni.m_option; |
rinosh2 | 0:1e951aba6a7e | 190 | int left = MAX_OPTION - 1; |
rinosh2 | 0:1e951aba6a7e | 191 | for(; opt < p ; ++opt){ |
rinosh2 | 0:1e951aba6a7e | 192 | // check option format |
rinosh2 | 0:1e951aba6a7e | 193 | int optID = getOptionID(*opt); |
rinosh2 | 0:1e951aba6a7e | 194 | if(optID < 0 || m_index[optID] == 0xff) return S_INVALID_OPTION_ID; |
rinosh2 | 0:1e951aba6a7e | 195 | |
rinosh2 | 0:1e951aba6a7e | 196 | // append option string |
rinosh2 | 0:1e951aba6a7e | 197 | char* src = m_optbuf + m_index[optID]; |
rinosh2 | 0:1e951aba6a7e | 198 | char* sep = strchr(src, ';'); |
rinosh2 | 0:1e951aba6a7e | 199 | int len; |
rinosh2 | 0:1e951aba6a7e | 200 | if(sep){ |
rinosh2 | 0:1e951aba6a7e | 201 | if(m_shortopt){ |
rinosh2 | 0:1e951aba6a7e | 202 | // abbreviation |
rinosh2 | 0:1e951aba6a7e | 203 | src = sep + 1; |
rinosh2 | 0:1e951aba6a7e | 204 | len = strlen(src); |
rinosh2 | 0:1e951aba6a7e | 205 | } else { |
rinosh2 | 0:1e951aba6a7e | 206 | // long description |
rinosh2 | 0:1e951aba6a7e | 207 | len = sep - src; |
rinosh2 | 0:1e951aba6a7e | 208 | } |
rinosh2 | 0:1e951aba6a7e | 209 | } else { |
rinosh2 | 0:1e951aba6a7e | 210 | // no separator |
rinosh2 | 0:1e951aba6a7e | 211 | len = strlen(src); |
rinosh2 | 0:1e951aba6a7e | 212 | } |
rinosh2 | 0:1e951aba6a7e | 213 | |
rinosh2 | 0:1e951aba6a7e | 214 | if(len > left) return S_OPTION_OVERFLOW; // MAX_OPTION is too small... |
rinosh2 | 0:1e951aba6a7e | 215 | memcpy(dst, src, len); |
rinosh2 | 0:1e951aba6a7e | 216 | dst += len; |
rinosh2 | 0:1e951aba6a7e | 217 | left -= len; |
rinosh2 | 0:1e951aba6a7e | 218 | } |
rinosh2 | 0:1e951aba6a7e | 219 | *dst = 0; |
rinosh2 | 0:1e951aba6a7e | 220 | return S_SUCCESS; |
rinosh2 | 0:1e951aba6a7e | 221 | } |
rinosh2 | 0:1e951aba6a7e | 222 | } |
rinosh2 | 0:1e951aba6a7e | 223 | break; |
rinosh2 | 0:1e951aba6a7e | 224 | |
rinosh2 | 0:1e951aba6a7e | 225 | case LINE_WEEK: |
rinosh2 | 0:1e951aba6a7e | 226 | week_mask = getWeekMask(line); |
rinosh2 | 0:1e951aba6a7e | 227 | break; |
rinosh2 | 0:1e951aba6a7e | 228 | |
rinosh2 | 0:1e951aba6a7e | 229 | case LINE_OPTION: |
rinosh2 | 0:1e951aba6a7e | 230 | // skip (use cache) |
rinosh2 | 0:1e951aba6a7e | 231 | break; |
rinosh2 | 0:1e951aba6a7e | 232 | } |
rinosh2 | 0:1e951aba6a7e | 233 | } |
rinosh2 | 0:1e951aba6a7e | 234 | |
rinosh2 | 0:1e951aba6a7e | 235 | return S_NO_TRAIN; // failed |
rinosh2 | 0:1e951aba6a7e | 236 | } |
rinosh2 | 0:1e951aba6a7e | 237 | |
rinosh2 | 0:1e951aba6a7e | 238 |