Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | #include <afsconfig.h> | |
11 | #include <afs/param.h> | |
12 | ||
13 | #include <roken.h> | |
14 | #include <afs/opr.h> | |
15 | ||
16 | #include <ctype.h> | |
17 | ||
18 | #include <afs/opr.h> | |
19 | #include "afsutil.h" | |
20 | #include "ktime.h" | |
21 | ||
22 | /* some date parsing routines */ | |
23 | ||
24 | struct token { | |
25 | struct token *next; | |
26 | char *key; | |
27 | }; | |
28 | ||
29 | static char *day[] = { | |
30 | "sun", | |
31 | "mon", | |
32 | "tue", | |
33 | "wed", | |
34 | "thu", | |
35 | "fri", | |
36 | "sat" | |
37 | }; | |
38 | ||
39 | /* free token list returned by parseLine */ | |
40 | static void | |
41 | LocalFreeTokens(struct token *alist) | |
42 | { | |
43 | struct token *nlist; | |
44 | for (; alist; alist = nlist) { | |
45 | nlist = alist->next; | |
46 | free(alist->key); | |
47 | free(alist); | |
48 | } | |
49 | return; | |
50 | } | |
51 | ||
52 | static int | |
53 | space(int x) | |
54 | { | |
55 | if (x == 0 || x == ' ' || x == '\t' || x == '\n') | |
56 | return 1; | |
57 | else | |
58 | return 0; | |
59 | } | |
60 | ||
61 | static int | |
62 | LocalParseLine(char *aline, struct token **alist) | |
63 | { | |
64 | char tbuffer[256]; | |
65 | char *tptr = NULL; | |
66 | int inToken; | |
67 | struct token *first, *last; | |
68 | struct token *ttok; | |
69 | int tc; | |
70 | ||
71 | inToken = 0; /* not copying token chars at start */ | |
72 | first = NULL; | |
73 | last = NULL; | |
74 | while (1) { | |
75 | tc = *aline++; | |
76 | if (tc == 0 || space(tc)) { /* terminating null gets us in here, too */ | |
77 | if (inToken) { | |
78 | inToken = 0; /* end of this token */ | |
79 | *tptr++ = 0; | |
80 | ttok = malloc(sizeof(struct token)); | |
81 | ttok->next = NULL; | |
82 | ttok->key = strdup(tbuffer); | |
83 | if (last) { | |
84 | last->next = ttok; | |
85 | last = ttok; | |
86 | } else | |
87 | last = ttok; | |
88 | if (!first) | |
89 | first = ttok; | |
90 | } | |
91 | } else { | |
92 | /* an alpha character */ | |
93 | if (!inToken) { | |
94 | tptr = tbuffer; | |
95 | inToken = 1; | |
96 | } | |
97 | if (tptr - tbuffer >= sizeof(tbuffer)) | |
98 | return -1; /* token too long */ | |
99 | *tptr++ = tc; | |
100 | } | |
101 | if (tc == 0) { | |
102 | /* last token flushed 'cause space(0) --> true */ | |
103 | if (last) | |
104 | last->next = NULL; | |
105 | *alist = first; | |
106 | return 0; | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | /* keyword database for periodic date parsing */ | |
112 | static struct ptemp { | |
113 | char *key; | |
114 | afs_int32 value; | |
115 | } ptkeys[] = { | |
116 | { | |
117 | "sun", 0x10000,}, { | |
118 | "mon", 0x10001,}, { | |
119 | "tue", 0x10002,}, { | |
120 | "wed", 0x10003,}, { | |
121 | "thu", 0x10004,}, { | |
122 | "fri", 0x10005,}, { | |
123 | "sat", 0x10006,}, { | |
124 | "sunday", 0x10000,}, { | |
125 | "monday", 0x10001,}, { | |
126 | "tuesday", 0x10002,}, { | |
127 | "wednesday", 0x10003,}, { | |
128 | "thursday", 0x10004,}, { | |
129 | "thur", 0x10004,}, { | |
130 | "friday", 0x10005,}, { | |
131 | "saturday", 0x10006,}, { | |
132 | "am", 0x20000,}, { | |
133 | "pm", 0x20001,}, { | |
134 | "a.m.", 0x20000,}, { | |
135 | "p.m.", 0x20001,}, { | |
136 | 0, 0,} | |
137 | }; | |
138 | ||
139 | /* ktime_DateOf | |
140 | * entry: | |
141 | * atime - time in seconds (Unix std) | |
142 | * exit: | |
143 | * return value - ptr to time in text form. Ptr is to a static string. | |
144 | */ | |
145 | ||
146 | char * | |
147 | ktime_DateOf(afs_int32 atime) | |
148 | { | |
149 | static char tbuffer[30]; | |
150 | char *tp; | |
151 | time_t t = atime; | |
152 | tp = ctime(&t); | |
153 | if (tp) { | |
154 | strcpy(tbuffer, tp); | |
155 | tbuffer[24] = 0; /* get rid of new line */ | |
156 | } else | |
157 | strcpy(tbuffer, "BAD TIME"); | |
158 | return tbuffer; | |
159 | } | |
160 | ||
161 | /* ParseTime | |
162 | * parse 12:33:12 or 12:33 or 12 into ktime structure | |
163 | * entry: | |
164 | * astr - ptr to string to be parsed | |
165 | * ak - ptr to struct for return value. | |
166 | * exit: | |
167 | * 0 - ak holds parsed time. | |
168 | * -1 - error in format | |
169 | */ | |
170 | ||
171 | static int | |
172 | ParseTime(struct ktime *ak, char *astr) | |
173 | { | |
174 | int field; | |
175 | afs_int32 temp; | |
176 | char *tp; | |
177 | int tc; | |
178 | ||
179 | field = 0; /* 0=hour, 1=min, 2=sec */ | |
180 | temp = 0; | |
181 | ||
182 | ak->mask |= (KTIME_HOUR | KTIME_MIN | KTIME_SEC); | |
183 | for (tp = astr;;) { | |
184 | tc = *tp++; | |
185 | if (tc == 0 || tc == ':') { | |
186 | if (field == 0) | |
187 | ak->hour = temp; | |
188 | else if (field == 1) | |
189 | ak->min = temp; | |
190 | else if (field == 2) | |
191 | ak->sec = temp; | |
192 | temp = 0; | |
193 | field++; | |
194 | if (tc == 0) | |
195 | break; | |
196 | continue; | |
197 | } else if (!isdigit(tc)) | |
198 | return -1; /* syntax error */ | |
199 | else { | |
200 | /* digit */ | |
201 | temp *= 10; | |
202 | temp += tc - '0'; | |
203 | } | |
204 | } | |
205 | if (ak->hour >= 24 || ak->min >= 60 || ak->sec >= 60) | |
206 | return -1; | |
207 | return 0; | |
208 | } | |
209 | ||
210 | afs_int32 | |
211 | ktime_Str2int32(char *astr) | |
212 | { | |
213 | struct ktime tk; | |
214 | ||
215 | memset(&tk, 0, sizeof(tk)); | |
216 | if (ParseTime(&tk, astr)) | |
217 | return (-1); /* syntax error */ | |
218 | ||
219 | return ((tk.hour * 60 + tk.min) * 60 + tk.sec); | |
220 | } | |
221 | ||
222 | /* ktime_ParsePeriodic | |
223 | * Parses periodic date of form | |
224 | * now | never | at [time spec] | every [time spec] | |
225 | * where [time spec] is a ktime string. | |
226 | * entry: | |
227 | * adate - string to be parsed | |
228 | * ak - ptr to structure for returned ktime | |
229 | * exit: | |
230 | * 0 - parsed ktime in ak | |
231 | * -1 - specification error | |
232 | */ | |
233 | ||
234 | /* -1 means error, 0 means now, otherwise returns time of next event */ | |
235 | int | |
236 | ktime_ParsePeriodic(char *adate, struct ktime *ak) | |
237 | { | |
238 | struct token *tt; | |
239 | afs_int32 code; | |
240 | struct ptemp *tp; | |
241 | ||
242 | memset(ak, 0, sizeof(*ak)); | |
243 | code = LocalParseLine(adate, &tt); | |
244 | if (code) | |
245 | return -1; | |
246 | for (; tt; tt = tt->next) { | |
247 | /* look at each token */ | |
248 | if (strcmp(tt->key, "now") == 0) { | |
249 | ak->mask |= KTIME_NOW; | |
250 | goto out; | |
251 | } | |
252 | if (strcmp(tt->key, "never") == 0) { | |
253 | ak->mask |= KTIME_NEVER; | |
254 | goto out; | |
255 | } | |
256 | if (strcmp(tt->key, "at") == 0) | |
257 | continue; | |
258 | if (strcmp(tt->key, "every") == 0) | |
259 | continue; | |
260 | if (isdigit(tt->key[0])) { | |
261 | /* parse a time */ | |
262 | code = ParseTime(ak, tt->key); | |
263 | if (code) { | |
264 | code = -1; | |
265 | goto out; | |
266 | } | |
267 | continue; | |
268 | } | |
269 | /* otherwise use keyword table */ | |
270 | for (tp = ptkeys;; tp++) { | |
271 | if (tp->key == NULL) { | |
272 | code = -1; | |
273 | goto out; | |
274 | } | |
275 | if (strcmp(tp->key, tt->key) == 0) | |
276 | break; | |
277 | } | |
278 | /* now look at tp->value to see what we've got */ | |
279 | if ((tp->value >> 16) == 1) { | |
280 | /* a day */ | |
281 | ak->mask |= KTIME_DAY; | |
282 | ak->day = tp->value & 0xff; | |
283 | } | |
284 | if ((tp->value >> 16) == 2) { | |
285 | /* am or pm token */ | |
286 | if ((tp->value & 0xff) == 1) { | |
287 | /* pm */ | |
288 | if (!(ak->mask & KTIME_HOUR)) { | |
289 | code = -1; | |
290 | goto out; | |
291 | } | |
292 | if (ak->hour < 12) { | |
293 | ak->hour += 12; | |
294 | /* 12 is 12 PM */ | |
295 | } else if (ak->hour != 12) { | |
296 | code = -1; | |
297 | goto out; | |
298 | } | |
299 | } else { | |
300 | /* am is almost a noop, except that we map 12:01 am to 0:01 */ | |
301 | if (ak->hour > 12) { | |
302 | code = -1; | |
303 | goto out; | |
304 | } | |
305 | if (ak->hour == 12) | |
306 | ak->hour = 0; | |
307 | } | |
308 | } | |
309 | } | |
310 | out: | |
311 | LocalFreeTokens(tt); | |
312 | return code; | |
313 | } | |
314 | ||
315 | /* ktime_DisplayString | |
316 | * Display ktime structure as English that could go into the ktime parser | |
317 | * entry: | |
318 | * aparm - ktime to be converted to string | |
319 | * astring - ptr to string, for the result | |
320 | * exit: | |
321 | * 0 - astring contains ktime string. | |
322 | */ | |
323 | int | |
324 | ktime_DisplayString(struct ktime *aparm, char *astring) | |
325 | { | |
326 | char tempString[50]; | |
327 | ||
328 | if (aparm->mask & KTIME_NEVER) { | |
329 | strcpy(astring, "never"); | |
330 | return 0; | |
331 | } else if (aparm->mask & KTIME_NOW) { | |
332 | strcpy(astring, "now"); | |
333 | return 0; | |
334 | } else { | |
335 | strcpy(astring, "at"); | |
336 | if (aparm->mask & KTIME_DAY) { | |
337 | strcat(astring, " "); | |
338 | strcat(astring, day[aparm->day]); | |
339 | } | |
340 | if (aparm->mask & KTIME_HOUR) { | |
341 | if (aparm->hour > 12) | |
342 | sprintf(tempString, " %d", aparm->hour - 12); | |
343 | else if (aparm->hour == 0) | |
344 | strcpy(tempString, " 12"); | |
345 | else | |
346 | sprintf(tempString, " %d", aparm->hour); | |
347 | strcat(astring, tempString); | |
348 | } | |
349 | if (aparm->mask & KTIME_MIN) { | |
350 | sprintf(tempString, ":%02d", aparm->min); | |
351 | strcat(astring, tempString); | |
352 | } | |
353 | if ((aparm->mask & KTIME_SEC) && aparm->sec != 0) { | |
354 | sprintf(tempString, ":%02d", aparm->sec); | |
355 | strcat(astring, tempString); | |
356 | } | |
357 | if (aparm->mask & KTIME_HOUR) { | |
358 | if (aparm->hour >= 12) | |
359 | strcat(astring, " pm"); | |
360 | else | |
361 | strcat(astring, " am"); | |
362 | } | |
363 | } | |
364 | return 0; | |
365 | } | |
366 | ||
367 | /* get next time that matches ktime structure after time afrom */ | |
368 | afs_int32 | |
369 | ktime_next(struct ktime * aktime, afs_int32 afrom) | |
370 | { | |
371 | /* try by guessing from now */ | |
372 | struct tm *tsp; | |
373 | time_t start; /* time to start looking */ | |
374 | time_t probe; /* a placeholder to use for advancing day to day */ | |
375 | time_t time_next; /* actual UTC time of probe, with time of day set */ | |
376 | afs_int32 tmask; | |
377 | struct ktime_date tdate; | |
378 | ||
379 | start = afrom + time(0); /* time to start search */ | |
380 | tmask = aktime->mask; | |
381 | ||
382 | /* handle some special cases */ | |
383 | if (tmask & KTIME_NEVER) | |
384 | return 0x7fffffff; | |
385 | if (tmask & KTIME_NOW) | |
386 | return 0; | |
387 | ||
388 | /* Use probe to fill in members of *tsp. Add 23 hours each iteration until | |
389 | * time_next is correct. Only add 23 hrs to avoid skipping spring | |
390 | * daylight savings time day */ | |
391 | for (probe = start;; probe += (23 * 3600)) { | |
392 | tsp = localtime(&probe); /* find out what UTC time "probe" is */ | |
393 | ||
394 | tdate.year = tsp->tm_year; | |
395 | tdate.month = tsp->tm_mon + 1; | |
396 | tdate.day = tsp->tm_mday; | |
397 | tdate.mask = | |
398 | KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR | |
399 | | KTIMEDATE_MIN | KTIMEDATE_SEC; | |
400 | tdate.hour = aktime->hour; /* edit in our changes */ | |
401 | tdate.min = aktime->min; | |
402 | tdate.sec = aktime->sec; | |
403 | time_next = ktime_InterpretDate(&tdate); /* Convert back to UTC time */ | |
404 | if (time_next < start) | |
405 | continue; /* "probe" time is already past */ | |
406 | if ((tmask & KTIME_DAY) == 0) /* don't care about day, we're done */ | |
407 | break; | |
408 | tsp = localtime(&time_next); | |
409 | if (tsp->tm_wday == aktime->day) | |
410 | break; /* day matches, we're done */ | |
411 | } | |
412 | return time_next; | |
413 | } | |
414 | ||
415 | ||
416 | /* compare date in both formats, and return as in strcmp */ | |
417 | #ifdef undef | |
418 | static int | |
419 | KTimeCmp(struct ktime *aktime, struct tm *atm) | |
420 | { | |
421 | afs_int32 tmask; | |
422 | ||
423 | /* don't compare day of the week, since we can't tell the | |
424 | * order in a cyclical set. Caller must check for equality, if | |
425 | * she cares */ | |
426 | tmask = aktime->mask; | |
427 | if (tmask & KTIME_HOUR) { | |
428 | if (aktime->hour > atm->tm_hour) | |
429 | return 1; | |
430 | if (aktime->hour < atm->tm_hour) | |
431 | return -1; | |
432 | } | |
433 | if (tmask & KTIME_MIN) { | |
434 | if (aktime->min > atm->tm_min) | |
435 | return 1; | |
436 | if (aktime->min < atm->tm_min) | |
437 | return -1; | |
438 | } | |
439 | if (tmask & KTIME_SEC) { | |
440 | if (aktime->sec > atm->tm_sec) | |
441 | return 1; | |
442 | if (aktime->sec < atm->tm_sec) | |
443 | return -1; | |
444 | } | |
445 | return 0; | |
446 | } | |
447 | #endif | |
448 | ||
449 | /* compare date in both formats, and return as in strcmp */ | |
450 | static int | |
451 | KDateCmp(struct ktime_date *akdate, struct tm *atm) | |
452 | { | |
453 | if (akdate->year > atm->tm_year) | |
454 | return 1; | |
455 | if (akdate->year < atm->tm_year) | |
456 | return -1; | |
457 | if (akdate->month > atm->tm_mon) | |
458 | return 1; | |
459 | if (akdate->month < atm->tm_mon) | |
460 | return -1; | |
461 | if (akdate->day > atm->tm_mday) | |
462 | return 1; | |
463 | if (akdate->day < atm->tm_mday) | |
464 | return -1; | |
465 | if (akdate->mask & KTIMEDATE_HOUR) { | |
466 | if (akdate->hour > atm->tm_hour) | |
467 | return 1; | |
468 | if (akdate->hour < atm->tm_hour) | |
469 | return -1; | |
470 | } | |
471 | if (akdate->mask & KTIMEDATE_MIN) { | |
472 | if (akdate->min > atm->tm_min) | |
473 | return 1; | |
474 | if (akdate->min < atm->tm_min) | |
475 | return -1; | |
476 | } | |
477 | if (akdate->mask & KTIMEDATE_SEC) { | |
478 | if (akdate->sec > atm->tm_sec) | |
479 | return 1; | |
480 | if (akdate->sec < atm->tm_sec) | |
481 | return -1; | |
482 | } | |
483 | return 0; | |
484 | } | |
485 | ||
486 | /* ktime_ParseDate | |
487 | * parse date string into ktime_date structure | |
488 | * entry: | |
489 | * adate - string to be parsed | |
490 | * akdate - ptr to ktime_date for result | |
491 | * exit: | |
492 | * 0 - akdate contains converted date | |
493 | * -1 - parsing failure | |
494 | */ | |
495 | ||
496 | static afs_int32 | |
497 | ktime_ParseDate(char *adate, struct ktime_date *akdate) | |
498 | { | |
499 | int code; | |
500 | afs_int32 month, day2, year, hour, min, sec; | |
501 | char never[7]; | |
502 | char c[2]; | |
503 | ||
504 | lcstring(never, adate, sizeof(never)); | |
505 | if (strcmp(never, "never") == 0) | |
506 | akdate->mask = KTIMEDATE_NEVER; | |
507 | else if (strcmp(never, "now") == 0) | |
508 | akdate->mask = KTIMEDATE_NOW; | |
509 | else | |
510 | akdate->mask = 0; | |
511 | if (akdate->mask) | |
512 | return 0; | |
513 | ||
514 | /* Old ambiguous mm/dd/yy hh:mm:ss format */ | |
515 | ||
516 | code = | |
517 | sscanf(adate, "%d / %d / %d %d : %d : %d%1s", &month, &day2, &year, | |
518 | &hour, &min, &sec, &c[0]); | |
519 | if (code != 6) { | |
520 | sec = 0; | |
521 | code = | |
522 | sscanf(adate, "%d / %d / %d %d : %d%1s", &month, &day2, &year, | |
523 | &hour, &min, &c[0]); | |
524 | if (code != 5) { | |
525 | hour = min = 0; | |
526 | code = | |
527 | sscanf(adate, "%d / %d / %d%1s", &month, &day2, &year, &c[0]); | |
528 | if (code != 3) { | |
529 | code = -1; | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | /* New ISO 8601 (subset) format */ | |
535 | ||
536 | if (code < 0) { | |
537 | hour = min = sec = 0; | |
538 | code = | |
539 | sscanf(adate, "%d-%d-%d %d:%d:%d%1s", &year, &month, &day2, | |
540 | &hour, &min, &sec, c); | |
541 | if (code != 3 && code != 5 && code != 6) | |
542 | code = -1; | |
543 | } | |
544 | ||
545 | if (code < 0) | |
546 | return code; | |
547 | ||
548 | if ((year < 0) || (month < 1) || (month > 12) || (day2 < 1) || (day2 > 31) || /* more or less */ | |
549 | (hour < 0) || (hour > 23) || (min < 0) || (min > 59) || (sec < 0) | |
550 | || (sec > 59)) | |
551 | return -2; | |
552 | ||
553 | if (year < 69) | |
554 | year += 100; /* make 1/1/1 => Jan 1, 2001 */ | |
555 | else if (year >= 1900) | |
556 | year -= 1900; /* allow 1/1/2001 to work */ | |
557 | else if (year > 99) | |
558 | return -2; /* only allow 2 or 4 digit years */ | |
559 | ||
560 | akdate->mask = | |
561 | KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR | | |
562 | KTIMEDATE_MIN | KTIMEDATE_SEC; | |
563 | ||
564 | akdate->year = year; | |
565 | akdate->month = month; | |
566 | akdate->day = day2; | |
567 | akdate->hour = hour; | |
568 | akdate->min = min; | |
569 | akdate->sec = sec; | |
570 | ||
571 | /* done successfully */ | |
572 | return 0; | |
573 | } | |
574 | ||
575 | /* ktime_DateToInt32 | |
576 | * Converts a ktime date string into an afs_int32 | |
577 | * entry: | |
578 | * adate - ktime date string | |
579 | * aint32 - ptr to afs_int32 | |
580 | * exit: | |
581 | * 0 - aint32 contains converted date. | |
582 | */ | |
583 | ||
584 | afs_int32 | |
585 | ktime_DateToInt32(char *adate, afs_int32 * aint32) | |
586 | { | |
587 | struct ktime_date tdate; | |
588 | afs_int32 code; | |
589 | unsigned long l; | |
590 | char c[2]; | |
591 | ||
592 | if (sscanf(adate, "%lu%1s", &l, c) == 1 && l > 200000000) | |
593 | *aint32 = l; | |
594 | else { | |
595 | /* parse the date into a ktime_date structure */ | |
596 | code = ktime_ParseDate(adate, &tdate); | |
597 | if (code) | |
598 | return code; /* failed to parse */ | |
599 | *aint32 = ktime_InterpretDate(&tdate); /* interpret as seconds since 1970 */ | |
600 | } | |
601 | ||
602 | return 0; | |
603 | } | |
604 | ||
605 | /* get useful error message to print about date input format */ | |
606 | char * | |
607 | ktime_GetDateUsage(void) | |
608 | { | |
609 | return "date format is '(yyyy-mm-dd | mm/dd/yy) [hh:mm]', using a 24 hour clock"; | |
610 | } | |
611 | ||
612 | ||
613 | /* ktime_InterpretDate | |
614 | * Converts ktime_date to an afs_int32 | |
615 | * entry: | |
616 | * akdate - date to be converted/interpreted | |
617 | * exit: | |
618 | * returns KTIMEDATE_NEVERDATE - if never flag was set, or | |
619 | * date converted to afs_int32. | |
620 | */ | |
621 | ||
622 | afs_int32 | |
623 | ktime_InterpretDate(struct ktime_date * akdate) | |
624 | { | |
625 | afs_uint32 tresult; | |
626 | afs_uint32 tbit; | |
627 | time_t temp; | |
628 | struct tm *tsp; | |
629 | ||
630 | if (akdate->mask & KTIMEDATE_NOW) | |
631 | return time(0); | |
632 | if (akdate->mask & KTIMEDATE_NEVER) | |
633 | return KTIMEDATE_NEVERDATE; | |
634 | ||
635 | tbit = 1 << 30; /* start off at max signed result */ | |
636 | tresult = 0; /* result to return */ | |
637 | while (tbit > 0) { | |
638 | temp = tresult + tbit; /* see if adding this bit keeps us < akdate */ | |
639 | tsp = localtime(&temp); | |
640 | tsp->tm_mon++; | |
641 | #ifdef notdef | |
642 | if (tsp->tm_mon == 0) { | |
643 | tsp->tm_mon = 12; | |
644 | tsp->tm_year--; | |
645 | } | |
646 | #endif | |
647 | if (KDateCmp(akdate, tsp) >= 0) { | |
648 | /* if temp still represents earlier than date than we're searching | |
649 | * for, add in bit as increment, otherwise use old value and look | |
650 | * for smaller increment */ | |
651 | tresult = temp; | |
652 | } | |
653 | tbit = tbit >> 1; /* try next bit */ | |
654 | } | |
655 | ||
656 | return tresult; | |
657 | } |