rinosh 2 / NextTrainFileLib
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NextTrainFile.cpp Source File

NextTrainFile.cpp

00001 ///////////////////////////////////////////////////////////////////////////////
00002 // NextTrainFile: NextTrain file parser   by rinos 2010
00003 ///////////////////////////////////////////////////////////////////////////////
00004 
00005 #include "NextTrainFile.h"
00006 #include <time.h>
00007 #include <stdlib.h>
00008 
00009 ////////////////////////////////////////////////////////////////////////////////
00010 // defines
00011 const int MAX_LINE_BUF = 256;
00012 
00013 ////////////////////////////////////////////////////////////////////////////////
00014 // NextTrainFile
00015 
00016 NextTrainFile::NextTrainFile(const char* ntFile) : m_fp(0), m_shortopt(false), m_delim(' ') {
00017     if(ntFile) open(ntFile);
00018 }
00019 
00020 NextTrainFile::~NextTrainFile(){
00021     close();
00022 }
00023 
00024 ////////////////////////////////////////////////////////////////////////////////
00025 // internal funcs
00026 
00027 int getLineType(char ch){
00028     if(ch == '#')   return NextTrainFile::LINE_TITLE;
00029     if(ch <  '0')   return NextTrainFile::LINE_COMMENT;
00030     if(ch <= '9')   return NextTrainFile::LINE_TIME;
00031     if(ch <  'A')   return NextTrainFile::LINE_COMMENT;
00032     if(ch <= 'Z')   return NextTrainFile::LINE_OPTION;
00033     if(ch == '[')   return NextTrainFile::LINE_WEEK;
00034     if(ch <  'a')   return NextTrainFile::LINE_COMMENT;
00035     if(ch <= 'z')   return NextTrainFile::LINE_OPTION;
00036     return NextTrainFile::LINE_COMMENT;
00037 }
00038 
00039 int getOptionID(char ch){
00040     if(ch <  'A')   return -1;
00041     if(ch <= 'Z')   return ch - 'A';
00042     if(ch <  'a')   return -1;
00043     if(ch <= 'z')   return ch - 'a' + 26;
00044     return -1;
00045 }
00046 
00047 const char* WEEK_LIST[] = { // week + holiday
00048     "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT", "HOL"
00049 };
00050 int getWeekMask(char* str){
00051     const char WEEK_DELIM[] = " []\t\r\n";
00052     
00053     int ret = 0;
00054     char * p = strtok(str, WEEK_DELIM);
00055     while(p){
00056         for(int index = 0; index < 8 ; ++index){ // week7 + holiday
00057             if(strcmp(p, WEEK_LIST[index]) == 0){ // stricmp?
00058                 ret |= 1 << index;
00059                 break;
00060             }
00061         }
00062         p = strtok(0, WEEK_DELIM);
00063     }
00064     return ret;
00065 }
00066 
00067 ////////////////////////////////////////////////////////////////////////////////
00068 // Open NextTrain format file and read option data
00069 NextTrainFile::Status NextTrainFile::open(const char* ntFile){
00070     Status ret = S_SUCCESS;
00071 
00072     // open check
00073     if(m_fp) return S_ALREADY_OPEN;
00074     m_fp = fopen(ntFile, "r");
00075     if(!m_fp) return S_OPEN_ERROR;
00076     
00077     // Read option info
00078     memset(m_index, 0xff, sizeof(m_index));
00079     int pos = 0;
00080     int left = MAX_OPTBUF - 1;
00081     char line[MAX_LINE_BUF];
00082     while(fgets(line, sizeof(line), m_fp)){
00083         switch(getLineType(*line)){
00084         case LINE_TITLE:
00085         case LINE_TIME:
00086         case LINE_WEEK:
00087             // Don't cache (for saving the memory)
00088             break;
00089 
00090         case LINE_OPTION:
00091             {
00092                 // Decode option / e.g. "a:Express;EX"
00093                 int optID = getOptionID(line[0]); // 0..51
00094                 if(optID < 0 || line[1] != ':') continue; // assert?
00095 
00096                 const char OPTION_DELIM[] = " \t\r\n";
00097                 char* p = strtok(line + 2, OPTION_DELIM);
00098                 if(!p) continue; // skip empty option
00099 
00100                 int len = strlen(p) + 1;
00101                 if(len > left){
00102                     ret = S_OPTION_OVERFLOW; // MAX_OPTBUF is too small...
00103                     continue;
00104                 }
00105                 m_index[optID] = (unsigned char)pos;
00106                 memcpy(m_optbuf + pos, p, len);
00107                 pos  += len;
00108                 left -= len;
00109             }
00110             break;
00111         }
00112     }
00113     printf("NextTrainFile::open return %d, read %d bytes\n", ret, pos);
00114     return ret;
00115 }
00116 
00117 NextTrainFile::Status NextTrainFile::close(){
00118     if(m_fp){
00119         fclose(m_fp); // no error check...
00120         m_fp = 0;
00121     }
00122     return S_SUCCESS;
00123 }
00124 
00125 NextTrainFile::Status NextTrainFile::search(time_t dt, int offset){
00126     if(!m_fp) return S_OPEN_ERROR; // Closed
00127 
00128     // Set target time/week
00129     if(!dt) dt = time(0); // get current time
00130     struct tm* st = localtime(&dt);
00131     int week_flag = 1 << st->tm_wday; // TODO HOLIDAY!
00132     unsigned int target = st->tm_hour;
00133     if(target < HOUR_DIV) target += 24;
00134     target = target * 60 + st->tm_min;
00135 
00136     // Scan from head of file
00137     rewind(m_fp);
00138     int week_mask = -1; // default: all
00139     char line[MAX_LINE_BUF];
00140     const char TIME_LINE_DELIM[] = " \t\r\n";
00141 
00142     while(fgets(line, sizeof(line), m_fp)){
00143         switch(getLineType(*line)){
00144         case LINE_TITLE:
00145             if(week_mask & week_flag) {
00146                 char* p1 = line + 1;
00147                 while(*p1     && strchr(TIME_LINE_DELIM, *p1)) ++p1;
00148                 char* p2 = p1 + strlen(p1);
00149                 while(p1 < p2 && strchr(TIME_LINE_DELIM, *p2)) --p2;
00150                 int len = p2 - p1;
00151                 if(*p1 && ++len > sizeof(m_ni.m_title) - 1){
00152                     len = sizeof(m_ni.m_title) - 1;
00153                 }
00154                 memcpy(m_ni.m_title, p1, len);
00155                 m_ni.m_title[len] = 0;
00156             }
00157             break;
00158 
00159         case LINE_TIME:
00160             // Decode option / e.g. "5: ic14 ie19 hc33 ie39 ie54"
00161             if(week_mask & week_flag){
00162                 // hour format check
00163                 char* p = strtok(line, ":");
00164                 if(!p) break; // Invalid hour line! (return error?)
00165                 int hour = atoi(p);
00166                 if(hour > 23 + HOUR_DIV) break; // Invalid time! (return error?)
00167 
00168                 // check hour
00169                 m_ni.m_hour = hour;
00170                 if(hour < HOUR_DIV) hour += 24;
00171                 unsigned int hm = hour * 60;
00172                 if(hm + 60 < target) break; // passed
00173 
00174                 // search target min
00175                 while((p = strtok(0, TIME_LINE_DELIM)) != 0){
00176                     // min format check
00177                     char* opt = p;
00178                     while(*p && !strchr("0123456789", *p)) ++p;
00179                     if(!*p) continue; // Invalid min format! (return error?)
00180                     m_ni.m_min = atoi(p);
00181                     if(m_ni.m_min >= 60) continue; // Invalid min format! (return error?)
00182 
00183                     // check min
00184                     if(hm + m_ni.m_min <= target) continue; // passed
00185 
00186                     if(offset-- > 0) continue; // view next
00187                     
00188                     // Found NextTrain
00189                     //m_ni.m_hour = hour; // use 24:00 instead of 00:00
00190                     m_ni.m_diff = (hm + m_ni.m_min - target) * 60 - st->tm_sec; // leap seconds (max 61)?
00191                     char* dst  = m_ni.m_option;
00192                     int   left = MAX_OPTION - 1;
00193                     for(; opt < p ; ++opt){
00194                         // check option format
00195                         int optID = getOptionID(*opt);
00196                         if(optID < 0 || m_index[optID] == 0xff){
00197                             printf("NextTrainFile::search S_INVALID_OPTION_ID %x\n", (optID < 0)? -1 : m_index[optID]);
00198                             return S_INVALID_OPTION_ID;
00199                         }
00200 
00201                         // append option string
00202                         char* src = m_optbuf + m_index[optID];
00203                         char* sep = strchr(src, ';');
00204                         int   len;
00205                         if(sep){
00206                             if(m_shortopt){
00207                                 // abbreviation
00208                                 src = sep + 1;
00209                                 len = strlen(src);
00210                             } else {
00211                                 // long description
00212                                 len = sep - src;
00213                             }
00214                         } else {
00215                             // no separator
00216                             len = strlen(src);
00217                         }
00218 
00219                         if(len + 1 > left){
00220                             printf("NextTrainFile::search S_OPTION_OVERFLOW\n");
00221                             return S_OPTION_OVERFLOW; // MAX_OPTION is too small...
00222                         }
00223                         if(dst != m_ni.m_option && m_delim){
00224                             *dst++ = m_delim;
00225                             left--;
00226                         }
00227                         memcpy(dst, src, len);
00228                         dst  += len;
00229                         left -= len;
00230                     }
00231                     *dst = 0;
00232                     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);
00233                     return S_SUCCESS;
00234                 }
00235             }
00236             break;
00237 
00238         case LINE_WEEK:
00239             week_mask = getWeekMask(line);
00240             break;
00241 
00242         case LINE_OPTION:
00243             // skip (use cache)
00244             break;
00245         }
00246     }
00247 
00248     printf("NextTrainFile::search %02d:%02d:%02d(%s) NoTrain\n", st->tm_hour, st->tm_min, st->tm_sec, WEEK_LIST[st->tm_wday]);
00249 
00250     return S_NO_TRAIN; // failed
00251 }
00252