| The Android Open Source Project | edbf3b6 | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  *  Copyright 2006 The Android Open Source Project | 
 | 3 |  */ | 
 | 4 |  | 
 | 5 | #include <pim/EventRecurrence.h> | 
 | 6 | #include <utils/String8.h> | 
 | 7 | #include <stdio.h> | 
 | 8 | #include <limits.h> | 
 | 9 |  | 
 | 10 | namespace android { | 
 | 11 |  | 
 | 12 | #define FAIL_HERE() do { \ | 
 | 13 |             printf("Parsing failed at line %d\n", __LINE__); \ | 
 | 14 |             return UNKNOWN_ERROR; \ | 
 | 15 |         } while(0) | 
 | 16 |  | 
 | 17 | EventRecurrence::EventRecurrence() | 
 | 18 |     :freq((freq_t)0), | 
 | 19 |      until(), | 
 | 20 |      count(0), | 
 | 21 |      interval(0), | 
 | 22 |      bysecond(0), | 
 | 23 |      bysecondCount(0), | 
 | 24 |      byminute(0), | 
 | 25 |      byminuteCount(0), | 
 | 26 |      byhour(0), | 
 | 27 |      byhourCount(0), | 
 | 28 |      byday(0), | 
 | 29 |      bydayNum(0), | 
 | 30 |      bydayCount(0), | 
 | 31 |      bymonthday(0), | 
 | 32 |      bymonthdayCount(0), | 
 | 33 |      byyearday(0), | 
 | 34 |      byyeardayCount(0), | 
 | 35 |      byweekno(0), | 
 | 36 |      byweeknoCount(0), | 
 | 37 |      bymonth(0), | 
 | 38 |      bymonthCount(0), | 
 | 39 |      bysetpos(0), | 
 | 40 |      bysetposCount(0), | 
 | 41 |      wkst(0) | 
 | 42 | { | 
 | 43 | } | 
 | 44 |  | 
 | 45 | EventRecurrence::~EventRecurrence() | 
 | 46 | { | 
 | 47 |     delete[] bysecond; | 
 | 48 |     delete[] byminute; | 
 | 49 |     delete[] byhour; | 
 | 50 |     delete[] byday; | 
 | 51 |     delete[] bydayNum; | 
 | 52 |     delete[] byyearday; | 
 | 53 |     delete[] bymonthday; | 
 | 54 |     delete[] byweekno; | 
 | 55 |     delete[] bymonth; | 
 | 56 |     delete[] bysetpos; | 
 | 57 | } | 
 | 58 |  | 
 | 59 | enum LHS { | 
 | 60 |     NONE_LHS = 0, | 
 | 61 |     FREQ, | 
 | 62 |     UNTIL, | 
 | 63 |     COUNT, | 
 | 64 |     INTERVAL, | 
 | 65 |     BYSECOND, | 
 | 66 |     BYMINUTE, | 
 | 67 |     BYHOUR, | 
 | 68 |     BYDAY, | 
 | 69 |     BYMONTHDAY, | 
 | 70 |     BYYEARDAY, | 
 | 71 |     BYWEEKNO, | 
 | 72 |     BYMONTH, | 
 | 73 |     BYSETPOS, | 
 | 74 |     WKST | 
 | 75 | }; | 
 | 76 |  | 
 | 77 | struct LHSProc | 
 | 78 | { | 
 | 79 |     const char16_t* text; | 
 | 80 |     size_t textSize; | 
 | 81 |     uint32_t value; | 
 | 82 | }; | 
 | 83 |  | 
 | 84 | const char16_t FREQ_text[] = { 'F', 'R', 'E', 'Q' }; | 
 | 85 | const char16_t UNTIL_text[] = { 'U', 'N', 'T', 'I', 'L' }; | 
 | 86 | const char16_t COUNT_text[] = { 'C', 'O', 'U', 'N', 'T' }; | 
 | 87 | const char16_t INTERVAL_text[] = { 'I', 'N', 'T', 'E', 'R', 'V', 'A', 'L'}; | 
 | 88 | const char16_t BYSECOND_text[] = { 'B', 'Y', 'S', 'E', 'C', 'O', 'N', 'D' }; | 
 | 89 | const char16_t BYMINUTE_text[] = { 'B', 'Y', 'M', 'I', 'N', 'U', 'T', 'E' }; | 
 | 90 | const char16_t BYHOUR_text[] = { 'B', 'Y', 'H', 'O', 'U', 'R' }; | 
 | 91 | const char16_t BYDAY_text[] = { 'B', 'Y', 'D', 'A', 'Y' }; | 
 | 92 | const char16_t BYMONTHDAY_text[] = { 'B','Y','M','O','N','T','H','D','A','Y' }; | 
 | 93 | const char16_t BYYEARDAY_text[] = { 'B','Y','Y','E','A','R','D','A','Y' }; | 
 | 94 | const char16_t BYWEEKNO_text[] = { 'B', 'Y', 'W', 'E', 'E', 'K', 'N', 'O' }; | 
 | 95 | const char16_t BYMONTH_text[] = { 'B', 'Y', 'M', 'O', 'N', 'T', 'H' }; | 
 | 96 | const char16_t BYSETPOS_text[] = { 'B', 'Y', 'S', 'E', 'T', 'P', 'O', 'S' }; | 
 | 97 | const char16_t WKST_text[] = { 'W', 'K', 'S', 'T' }; | 
 | 98 |  | 
 | 99 | #define SIZ(x) (sizeof(x)/sizeof(x[0])) | 
 | 100 |  | 
 | 101 | const LHSProc LHSPROC[] = { | 
 | 102 |     { FREQ_text, SIZ(FREQ_text), FREQ }, | 
 | 103 |     { UNTIL_text, SIZ(UNTIL_text), UNTIL }, | 
 | 104 |     { COUNT_text, SIZ(COUNT_text), COUNT }, | 
 | 105 |     { INTERVAL_text, SIZ(INTERVAL_text), INTERVAL }, | 
 | 106 |     { BYSECOND_text, SIZ(BYSECOND_text), BYSECOND }, | 
 | 107 |     { BYMINUTE_text, SIZ(BYMINUTE_text), BYMINUTE }, | 
 | 108 |     { BYHOUR_text, SIZ(BYHOUR_text), BYHOUR }, | 
 | 109 |     { BYDAY_text, SIZ(BYDAY_text), BYDAY }, | 
 | 110 |     { BYMONTHDAY_text, SIZ(BYMONTHDAY_text), BYMONTHDAY }, | 
 | 111 |     { BYYEARDAY_text, SIZ(BYYEARDAY_text), BYYEARDAY }, | 
 | 112 |     { BYWEEKNO_text, SIZ(BYWEEKNO_text), BYWEEKNO }, | 
 | 113 |     { BYMONTH_text, SIZ(BYMONTH_text), BYMONTH }, | 
 | 114 |     { BYSETPOS_text, SIZ(BYSETPOS_text), BYSETPOS }, | 
 | 115 |     { WKST_text, SIZ(WKST_text), WKST }, | 
 | 116 |     { NULL, 0, NONE_LHS }, | 
 | 117 | }; | 
 | 118 |  | 
 | 119 | const char16_t SECONDLY_text[] = { 'S','E','C','O','N','D','L','Y' }; | 
 | 120 | const char16_t MINUTELY_text[] = { 'M','I','N','U','T','E','L','Y' }; | 
 | 121 | const char16_t HOURLY_text[] = { 'H','O','U','R','L','Y' }; | 
 | 122 | const char16_t DAILY_text[] = { 'D','A','I','L','Y' }; | 
 | 123 | const char16_t WEEKLY_text[] = { 'W','E','E','K','L','Y' }; | 
 | 124 | const char16_t MONTHLY_text[] = { 'M','O','N','T','H','L','Y' }; | 
 | 125 | const char16_t YEARLY_text[] = { 'Y','E','A','R','L','Y' }; | 
 | 126 |  | 
 | 127 | typedef LHSProc FreqProc; | 
 | 128 |  | 
 | 129 | const FreqProc FREQPROC[] = { | 
 | 130 |     { SECONDLY_text, SIZ(SECONDLY_text), EventRecurrence::SECONDLY }, | 
 | 131 |     { MINUTELY_text, SIZ(MINUTELY_text), EventRecurrence::MINUTELY }, | 
 | 132 |     { HOURLY_text, SIZ(HOURLY_text), EventRecurrence::HOURLY }, | 
 | 133 |     { DAILY_text, SIZ(DAILY_text), EventRecurrence::DAILY }, | 
 | 134 |     { WEEKLY_text, SIZ(WEEKLY_text), EventRecurrence::WEEKLY }, | 
 | 135 |     { MONTHLY_text, SIZ(MONTHLY_text), EventRecurrence::MONTHLY }, | 
 | 136 |     { YEARLY_text, SIZ(YEARLY_text), EventRecurrence::YEARLY }, | 
 | 137 |     { NULL, 0, NONE_LHS }, | 
 | 138 | }; | 
 | 139 |  | 
 | 140 | const char16_t SU_text[] = { 'S','U' }; | 
 | 141 | const char16_t MO_text[] = { 'M','O' }; | 
 | 142 | const char16_t TU_text[] = { 'T','U' }; | 
 | 143 | const char16_t WE_text[] = { 'W','E' }; | 
 | 144 | const char16_t TH_text[] = { 'T','H' }; | 
 | 145 | const char16_t FR_text[] = { 'F','R' }; | 
 | 146 | const char16_t SA_text[] = { 'S','A' }; | 
 | 147 |  | 
 | 148 | const FreqProc WEEKDAYPROC[] = { | 
 | 149 |     { SU_text, SIZ(SU_text), EventRecurrence::SU }, | 
 | 150 |     { MO_text, SIZ(MO_text), EventRecurrence::MO }, | 
 | 151 |     { TU_text, SIZ(TU_text), EventRecurrence::TU }, | 
 | 152 |     { WE_text, SIZ(WE_text), EventRecurrence::WE }, | 
 | 153 |     { TH_text, SIZ(TH_text), EventRecurrence::TH }, | 
 | 154 |     { FR_text, SIZ(FR_text), EventRecurrence::FR }, | 
 | 155 |     { SA_text, SIZ(SA_text), EventRecurrence::SA }, | 
 | 156 |     { NULL, 0, NONE_LHS }, | 
 | 157 | }; | 
 | 158 |  | 
 | 159 | // returns the index into LHSPROC for the match or -1 if not found | 
 | 160 | inline static int | 
 | 161 | match_proc(const LHSProc* p, const char16_t* str, size_t len) | 
 | 162 | { | 
 | 163 |     int i = 0; | 
 | 164 |     while (p->text != NULL) { | 
 | 165 |         if (p->textSize == len) { | 
 | 166 |             if (0 == memcmp(p->text, str, len*sizeof(char16_t))) { | 
 | 167 |                 return i; | 
 | 168 |             } | 
 | 169 |         } | 
 | 170 |         p++; | 
 | 171 |         i++; | 
 | 172 |     } | 
 | 173 |     return -1; | 
 | 174 | } | 
 | 175 |  | 
 | 176 | // rangeMin and rangeMax are inclusive | 
 | 177 | static status_t | 
 | 178 | parse_int(const char16_t* str, size_t len, int* out, | 
 | 179 |             int rangeMin, int rangeMax, bool zeroOK) | 
 | 180 | { | 
 | 181 |     char16_t c; | 
 | 182 |     size_t i=0; | 
 | 183 |  | 
 | 184 |     if (len == 0) { | 
 | 185 |         FAIL_HERE(); | 
 | 186 |     } | 
 | 187 |     bool negative = false; | 
 | 188 |     c = str[0]; | 
 | 189 |     if (c == '-' ) { | 
 | 190 |         negative = true; | 
 | 191 |         i++; | 
 | 192 |     } | 
 | 193 |     else if (c == '+') { | 
 | 194 |         i++; | 
 | 195 |     } | 
 | 196 |     int n = 0; | 
 | 197 |     for (; i<len; i++) { | 
 | 198 |         c = str[i]; | 
 | 199 |         if (c < '0' || c > '9') { | 
 | 200 |             FAIL_HERE(); | 
 | 201 |         } | 
 | 202 |         int prev = n; | 
 | 203 |         n *= 10; | 
 | 204 |         // the spec doesn't address how big these numbers can be, | 
 | 205 |         // so we're not going to worry about not being able to represent | 
 | 206 |         // INT_MIN, and if we're going to wrap, we'll just clamp to | 
 | 207 |         // INT_MAX instead | 
 | 208 |         if (n < prev) { | 
 | 209 |             n = INT_MAX; | 
 | 210 |         } else { | 
 | 211 |             n += c - '0'; | 
 | 212 |         } | 
 | 213 |     } | 
 | 214 |     if (negative) { | 
 | 215 |         n = -n; | 
 | 216 |     } | 
 | 217 |     if (n < rangeMin || n > rangeMax) { | 
 | 218 |         FAIL_HERE(); | 
 | 219 |     } | 
 | 220 |     if (!zeroOK && n == 0) { | 
 | 221 |         FAIL_HERE(); | 
 | 222 |     } | 
 | 223 |     *out = n; | 
 | 224 |     return NO_ERROR; | 
 | 225 | } | 
 | 226 |  | 
 | 227 | static status_t | 
 | 228 | parse_int_list(const char16_t* str, size_t len, int* countOut, int** listOut, | 
 | 229 |           int rangeMin, int rangeMax, bool zeroOK, | 
 | 230 |           status_t (*func)(const char16_t*,size_t,int*,int,int,bool)=parse_int) | 
 | 231 | { | 
 | 232 |     status_t err; | 
 | 233 |  | 
 | 234 |     if (len == 0) { | 
 | 235 |         *countOut = 0; | 
 | 236 |         *listOut = NULL; | 
 | 237 |         return NO_ERROR; | 
 | 238 |     } | 
 | 239 |  | 
 | 240 |     // make one pass through looking for commas so we know how big to make our | 
 | 241 |     // out array. | 
 | 242 |     int count = 1; | 
 | 243 |     for (size_t i=0; i<len; i++) { | 
 | 244 |         if (str[i] == ',') { | 
 | 245 |             count++; | 
 | 246 |         } | 
 | 247 |     } | 
 | 248 |  | 
 | 249 |     int* list = new int[count]; | 
 | 250 |     const char16_t* p = str; | 
 | 251 |     int commaIndex = 0; | 
 | 252 |     size_t i; | 
 | 253 |  | 
 | 254 |     for (i=0; i<len; i++) { | 
 | 255 |         if (str[i] == ',') { | 
 | 256 |             err = func(p, (str+i-p), list+commaIndex, rangeMin, | 
 | 257 |                     rangeMax, zeroOK); | 
 | 258 |             if (err != NO_ERROR) { | 
 | 259 |                 goto bail; | 
 | 260 |             } | 
 | 261 |             commaIndex++; | 
 | 262 |             p = str+i+1; | 
 | 263 |         } | 
 | 264 |     } | 
 | 265 |  | 
 | 266 |     err = func(p, (str+i-p), list+commaIndex, rangeMin, rangeMax, zeroOK); | 
 | 267 |     if (err != NO_ERROR) { | 
 | 268 |         goto bail; | 
 | 269 |     } | 
 | 270 |     commaIndex++; | 
 | 271 |  | 
 | 272 |     *countOut = count; | 
 | 273 |     *listOut = list; | 
 | 274 |  | 
 | 275 |     return NO_ERROR; | 
 | 276 |  | 
 | 277 | bail: | 
 | 278 |     delete[] list; | 
 | 279 |     FAIL_HERE(); | 
 | 280 | } | 
 | 281 |  | 
 | 282 | // the numbers here are small, so we pack them both into one value, and then | 
 | 283 | // split it out later.  it lets us reuse all the comma separated list code. | 
 | 284 | static status_t | 
 | 285 | parse_byday(const char16_t* s, size_t len, int* out, | 
 | 286 |             int rangeMin, int rangeMax, bool zeroOK) | 
 | 287 | { | 
 | 288 |     status_t err; | 
 | 289 |     int n = 0; | 
 | 290 |     const char16_t* p = s; | 
 | 291 |     size_t plen = len; | 
 | 292 |  | 
 | 293 |     if (len > 0) { | 
 | 294 |         char16_t c = s[0]; | 
 | 295 |         if (c == '-' || c == '+' || (c >= '0' && c <= '9')) { | 
 | 296 |             if (len > 1) { | 
 | 297 |                 size_t nlen = 0; | 
 | 298 |                 c = s[nlen]; | 
 | 299 |                 while (nlen < len | 
 | 300 |                         && (c == '-' || c == '+' || (c >= '0' && c <= '9'))) { | 
 | 301 |                     c = s[nlen]; | 
 | 302 |                     nlen++; | 
 | 303 |                 } | 
 | 304 |                 if (nlen > 0) { | 
 | 305 |                     nlen--; | 
 | 306 |                     err = parse_int(s, nlen, &n, rangeMin, rangeMax, zeroOK); | 
 | 307 |                     if (err != NO_ERROR) { | 
 | 308 |                         FAIL_HERE(); | 
 | 309 |                     } | 
 | 310 |                     p += nlen; | 
 | 311 |                     plen -= nlen; | 
 | 312 |                 } | 
 | 313 |             } | 
 | 314 |         } | 
 | 315 |  | 
 | 316 |         int index = match_proc(WEEKDAYPROC, p, plen); | 
 | 317 |         if (index >= 0) { | 
 | 318 |             *out = (0xffff0000 & WEEKDAYPROC[index].value) | 
 | 319 |                     | (0x0000ffff & n); | 
 | 320 |             return NO_ERROR; | 
 | 321 |         } | 
 | 322 |     } | 
 | 323 |     return UNKNOWN_ERROR; | 
 | 324 | } | 
 | 325 |  | 
 | 326 | static void | 
 | 327 | postprocess_byday(int count, int* byday, int** bydayNum) | 
 | 328 | { | 
 | 329 |     int* bdn = new int[count]; | 
 | 330 |     *bydayNum = bdn; | 
 | 331 |     for (int i=0; i<count; i++) { | 
 | 332 |         uint32_t v = byday[i]; | 
 | 333 |         int16_t num = v & 0x0000ffff; | 
 | 334 |         byday[i] = v & 0xffff0000;   | 
 | 335 |         // will sign extend: | 
 | 336 |         bdn[i] = num; | 
 | 337 |     } | 
 | 338 | } | 
 | 339 |  | 
 | 340 | #define PARSE_INT_LIST_CHECKED(name, rangeMin, rangeMax, zeroOK) \ | 
 | 341 |     if (name##Count != 0 || NO_ERROR != parse_int_list(s, slen, \ | 
 | 342 |                          &name##Count, &name, rangeMin, rangeMax, zeroOK)) { \ | 
 | 343 |         FAIL_HERE(); \ | 
 | 344 |     } | 
 | 345 | status_t | 
 | 346 | EventRecurrence::parse(const String16& str) | 
 | 347 | { | 
 | 348 |     char16_t const* work = str.string(); | 
 | 349 |     size_t len = str.size(); | 
 | 350 |  | 
 | 351 |     int lhsIndex = NONE_LHS; | 
 | 352 |     int index; | 
 | 353 |      | 
 | 354 |     size_t start = 0; | 
 | 355 |     for (size_t i=0; i<len; i++) { | 
 | 356 |         char16_t c = work[i]; | 
 | 357 |         if (c != ';' && i == len-1) { | 
 | 358 |             c = ';'; | 
 | 359 |             i++; | 
 | 360 |         } | 
 | 361 |         if (c == ';' || c == '=') { | 
 | 362 |             if (i != start) { | 
 | 363 |                 const char16_t* s = work+start; | 
 | 364 |                 const size_t slen = i-start; | 
 | 365 |  | 
 | 366 |                 String8 thestring(String16(s, slen)); | 
 | 367 |  | 
 | 368 |                 switch (c) | 
 | 369 |                 { | 
 | 370 |                     case '=': | 
 | 371 |                         if (lhsIndex == NONE_LHS) { | 
 | 372 |                             lhsIndex = match_proc(LHSPROC, s, slen); | 
 | 373 |                             if (lhsIndex >= 0) { | 
 | 374 |                                 break; | 
 | 375 |                             } | 
 | 376 |                         } | 
 | 377 |                         FAIL_HERE(); | 
 | 378 |                     case ';': | 
 | 379 |                     { | 
 | 380 |                         switch (LHSPROC[lhsIndex].value) | 
 | 381 |                         { | 
 | 382 |                             case FREQ: | 
 | 383 |                                 if (this->freq != 0) { | 
 | 384 |                                     FAIL_HERE(); | 
 | 385 |                                 } | 
 | 386 |                                 index = match_proc(FREQPROC, s, slen); | 
 | 387 |                                 if (index >= 0) { | 
 | 388 |                                     this->freq = (freq_t)FREQPROC[index].value; | 
 | 389 |                                 } | 
 | 390 |                                 break; | 
 | 391 |                             case UNTIL: | 
 | 392 |                                 // XXX should check that this is a valid time | 
 | 393 |                                 until.setTo(String16(s, slen)); | 
 | 394 |                                 break; | 
 | 395 |                             case COUNT: | 
 | 396 |                                 if (count != 0 | 
 | 397 |                                      || NO_ERROR != parse_int(s, slen, | 
 | 398 |                                              &count, INT_MIN, INT_MAX, true)) { | 
 | 399 |                                     FAIL_HERE(); | 
 | 400 |                                 } | 
 | 401 |                                 break; | 
 | 402 |                             case INTERVAL: | 
 | 403 |                                 if (interval != 0 | 
 | 404 |                                      || NO_ERROR != parse_int(s, slen, | 
 | 405 |                                          &interval, INT_MIN, INT_MAX, false)) { | 
 | 406 |                                     FAIL_HERE(); | 
 | 407 |                                 } | 
 | 408 |                                 break; | 
 | 409 |                             case BYSECOND: | 
 | 410 |                                 PARSE_INT_LIST_CHECKED(bysecond, 0, 59, true) | 
 | 411 |                                 break; | 
 | 412 |                             case BYMINUTE: | 
 | 413 |                                 PARSE_INT_LIST_CHECKED(byminute, 0, 59, true) | 
 | 414 |                                 break; | 
 | 415 |                             case BYHOUR: | 
 | 416 |                                 PARSE_INT_LIST_CHECKED(byhour, 0, 23, true) | 
 | 417 |                                 break; | 
 | 418 |                             case BYDAY: | 
 | 419 |                                 if (bydayCount != 0 || NO_ERROR !=  | 
 | 420 |                                         parse_int_list(s, slen, &bydayCount, | 
 | 421 |                                               &byday, -53, 53, false, | 
 | 422 |                                               parse_byday)) { | 
 | 423 |                                     FAIL_HERE(); | 
 | 424 |                                 } | 
 | 425 |                                 postprocess_byday(bydayCount, byday, &bydayNum); | 
 | 426 |                                 break; | 
 | 427 |                             case BYMONTHDAY: | 
 | 428 |                                 PARSE_INT_LIST_CHECKED(bymonthday, -31, 31, | 
 | 429 |                                                         false) | 
 | 430 |                                 break; | 
 | 431 |                             case BYYEARDAY: | 
 | 432 |                                 PARSE_INT_LIST_CHECKED(byyearday, -366, 366, | 
 | 433 |                                                         false) | 
 | 434 |                                 break; | 
 | 435 |                             case BYWEEKNO: | 
 | 436 |                                 PARSE_INT_LIST_CHECKED(byweekno, -53, 53, | 
 | 437 |                                                         false) | 
 | 438 |                                 break; | 
 | 439 |                             case BYMONTH: | 
 | 440 |                                 PARSE_INT_LIST_CHECKED(bymonth, 1, 12, false) | 
 | 441 |                                 break; | 
 | 442 |                             case BYSETPOS: | 
 | 443 |                                 PARSE_INT_LIST_CHECKED(bysetpos, | 
 | 444 |                                                         INT_MIN, INT_MAX, true) | 
 | 445 |                                 break; | 
 | 446 |                             case WKST: | 
 | 447 |                                 if (this->wkst != 0) { | 
 | 448 |                                     FAIL_HERE(); | 
 | 449 |                                 } | 
 | 450 |                                 index = match_proc(WEEKDAYPROC, s, slen); | 
 | 451 |                                 if (index >= 0) { | 
 | 452 |                                     this->wkst = (int)WEEKDAYPROC[index].value; | 
 | 453 |                                 } | 
 | 454 |                                 break; | 
 | 455 |                             default: | 
 | 456 |                                 FAIL_HERE(); | 
 | 457 |                         } | 
 | 458 |                         lhsIndex = NONE_LHS; | 
 | 459 |                         break; | 
 | 460 |                     } | 
 | 461 |                 } | 
 | 462 |  | 
 | 463 |                 start = i+1; | 
 | 464 |             } | 
 | 465 |         } | 
 | 466 |     } | 
 | 467 |  | 
 | 468 |     // enforce that there was a FREQ | 
 | 469 |     if (freq == 0) { | 
 | 470 |         FAIL_HERE(); | 
 | 471 |     } | 
 | 472 |  | 
 | 473 |     // default wkst to MO if it wasn't specified | 
 | 474 |     if (wkst == 0) { | 
 | 475 |         wkst = MO; | 
 | 476 |     } | 
 | 477 |  | 
 | 478 |     return NO_ERROR; | 
 | 479 | } | 
 | 480 |  | 
 | 481 |  | 
 | 482 | }; // namespace android | 
 | 483 |  | 
 | 484 |  |