NextTrain format file library

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?

UserRevisionLine numberNew 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