e41694544bebf140701ad99a28d6904e7a77dbc9
[krb5.git] / src / windows / identity / kcreddb / type.c
1 /*\r
2  * Copyright (c) 2004 Massachusetts Institute of Technology\r
3  *\r
4  * Permission is hereby granted, free of charge, to any person\r
5  * obtaining a copy of this software and associated documentation\r
6  * files (the "Software"), to deal in the Software without\r
7  * restriction, including without limitation the rights to use, copy,\r
8  * modify, merge, publish, distribute, sublicense, and/or sell copies\r
9  * of the Software, and to permit persons to whom the Software is\r
10  * furnished to do so, subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be\r
13  * included in all copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
22  * SOFTWARE.\r
23  */\r
24 \r
25 /* $Id$ */\r
26 \r
27 #include<kcreddbinternal.h>\r
28 #include<limits.h>\r
29 \r
30 CRITICAL_SECTION cs_type;\r
31 hashtable * kcdb_type_namemap;\r
32 kcdb_type_i ** kcdb_type_tbl;\r
33 kcdb_type_i * kcdb_types = NULL;\r
34 \r
35 /* Void */\r
36 \r
37 #define GENERIC_VOID_STR L"(Void)"\r
38 \r
39 khm_int32 KHMAPI kcdb_type_void_toString(\r
40     const void * d, \r
41     khm_size cbd, \r
42     wchar_t * buffer, \r
43     khm_size * cb_buf, \r
44     khm_int32 flags)\r
45 {\r
46     size_t cbsize;\r
47 \r
48     if(!cb_buf)\r
49         return KHM_ERROR_INVALID_PARM;\r
50 \r
51     cbsize = sizeof(GENERIC_VOID_STR);\r
52 \r
53     if(!buffer || *cb_buf < cbsize) {\r
54         *cb_buf = cbsize;\r
55         return KHM_ERROR_TOO_LONG;\r
56     }\r
57 \r
58     StringCbCopy(buffer, *cb_buf, GENERIC_VOID_STR);\r
59 \r
60     *cb_buf = cbsize;\r
61 \r
62     return KHM_ERROR_SUCCESS;\r
63 }\r
64 \r
65 khm_boolean KHMAPI kcdb_type_void_isValid(\r
66     const void * d,\r
67     khm_size cbd)\r
68 {\r
69     /* void is always valid, even if d is NULL */\r
70     return TRUE;\r
71 }\r
72 \r
73 khm_int32 KHMAPI kcdb_type_void_comp(\r
74     const void * d1,\r
75     khm_size cbd1,\r
76     const void * d2,\r
77     khm_size cbd2)\r
78 {\r
79     /* voids can not be compared */\r
80     return 0;\r
81 }\r
82 \r
83 khm_int32 KHMAPI kcdb_type_void_dup(\r
84     const void * d_src,\r
85     khm_size cbd_src,\r
86     void * d_dst,\r
87     khm_size * cbd_dst)\r
88 {\r
89     if(!cbd_dst)\r
90         return KHM_ERROR_INVALID_PARM;\r
91 \r
92     *cbd_dst = 0;\r
93 \r
94     /* copying a void doesn't do much */\r
95     return KHM_ERROR_SUCCESS;\r
96 }\r
97 \r
98 \r
99 /* String */\r
100 khm_int32 KHMAPI kcdb_type_string_toString(\r
101     const void * d, \r
102     khm_size cbd, \r
103     wchar_t * buffer, \r
104     khm_size * cb_buf, \r
105     khm_int32 flags)\r
106 {\r
107     size_t cbsize;\r
108     wchar_t * sd;\r
109 \r
110     if(!cb_buf)\r
111         return KHM_ERROR_INVALID_PARM;\r
112 \r
113     sd = (wchar_t *) d;\r
114 \r
115     if(FAILED(StringCbLength(sd, KCDB_TYPE_MAXCB, &cbsize)))\r
116         return KHM_ERROR_INVALID_PARM;\r
117 \r
118     cbsize += sizeof(wchar_t);\r
119 \r
120     if(!buffer || *cb_buf < cbsize) {\r
121         *cb_buf = cbsize;\r
122         return KHM_ERROR_TOO_LONG;\r
123     }\r
124 \r
125     StringCbCopy(buffer, *cb_buf, sd);\r
126 \r
127     *cb_buf = cbsize;\r
128 \r
129     return KHM_ERROR_SUCCESS;\r
130 }\r
131 \r
132 khm_boolean KHMAPI kcdb_type_string_isValid(\r
133     const void * d,\r
134     khm_size cbd)\r
135 {\r
136     size_t cbsize;\r
137 \r
138     if(cbd == KCDB_CBSIZE_AUTO)\r
139         cbd = KCDB_TYPE_MAXCB;\r
140 \r
141     if(FAILED(StringCbLength((wchar_t *) d, cbd, &cbsize)))\r
142         return FALSE;\r
143     else\r
144         return TRUE;\r
145 }\r
146 \r
147 khm_int32 KHMAPI kcdb_type_string_comp(\r
148     const void * d1,\r
149     khm_size cbd1,\r
150     const void * d2,\r
151     khm_size cbd2)\r
152 {\r
153     return wcscmp((const wchar_t *) d1, (const wchar_t *) d2);\r
154 }\r
155 \r
156 khm_int32 KHMAPI kcdb_type_string_dup(\r
157     const void * d_src,\r
158     khm_size cbd_src,\r
159     void * d_dst,\r
160     khm_size * cbd_dst)\r
161 {\r
162     size_t cbsize;\r
163 \r
164     if(!cbd_dst)\r
165         return KHM_ERROR_INVALID_PARM;\r
166 \r
167     if(cbd_src == KCDB_CBSIZE_AUTO) {\r
168         cbd_src = KCDB_TYPE_MAXCB;\r
169     }\r
170 \r
171     if(FAILED(StringCbLength((const wchar_t *) d_src, cbd_src, &cbsize))) {\r
172         return KHM_ERROR_UNKNOWN;\r
173     }\r
174 \r
175     cbsize += sizeof(wchar_t);\r
176 \r
177     if(!d_dst || *cbd_dst < cbsize) {\r
178         *cbd_dst = cbsize;\r
179         return KHM_ERROR_TOO_LONG;\r
180     }\r
181 \r
182     StringCbCopy((wchar_t *) d_dst, *cbd_dst, (const wchar_t *) d_src);\r
183     *cbd_dst = cbsize;\r
184 \r
185     return KHM_ERROR_SUCCESS;\r
186 }\r
187 \r
188 /* Date and time */\r
189 \r
190 \r
191 khm_int32 KHMAPI kcdb_type_date_toString(\r
192     const void * d, \r
193     khm_size cbd, \r
194     wchar_t * buffer, \r
195     khm_size * cb_buf, \r
196     khm_int32 flags)\r
197 {\r
198     size_t cbsize;\r
199     size_t cchsize;\r
200     wchar_t * bufend;\r
201     SYSTEMTIME st_now;\r
202     SYSTEMTIME st_d;\r
203     SYSTEMTIME st_dl;\r
204     FILETIME *ft;\r
205     int today = 0;\r
206 \r
207     if(!cb_buf)\r
208         return KHM_ERROR_INVALID_PARM;\r
209 \r
210     ft = (FILETIME *) d;\r
211 \r
212     GetLocalTime(&st_now);\r
213     FileTimeToSystemTime(ft, &st_d);\r
214     SystemTimeToTzSpecificLocalTime(NULL, &st_d, &st_dl);\r
215     if(st_now.wYear == st_dl.wYear &&\r
216         st_now.wMonth == st_dl.wMonth &&\r
217         st_now.wDay == st_dl.wDay)\r
218         today = 1;\r
219 \r
220     if(today && (flags & KCDB_TS_SHORT)) {\r
221         cbsize = 0;\r
222     } else {\r
223         cbsize = GetDateFormat(\r
224             LOCALE_USER_DEFAULT,\r
225             DATE_SHORTDATE,\r
226             &st_dl,\r
227             NULL,\r
228             NULL,\r
229             0) * sizeof(wchar_t);\r
230         cbsize += sizeof(wchar_t);\r
231     }\r
232 \r
233     cbsize += GetTimeFormat(\r
234         LOCALE_USER_DEFAULT,\r
235         0,\r
236         &st_dl,\r
237         NULL,\r
238         NULL,\r
239         0) * sizeof(wchar_t);\r
240 \r
241     cbsize += sizeof(wchar_t);\r
242 \r
243     if(!buffer || *cb_buf < cbsize) {\r
244         *cb_buf = cbsize;\r
245         return KHM_ERROR_TOO_LONG;\r
246     }\r
247 \r
248     cchsize = cbsize / sizeof(wchar_t);\r
249 \r
250     if(!today || !(flags & KCDB_TS_SHORT)) {\r
251         size_t cch_buf_len;\r
252 \r
253         GetDateFormat(\r
254             LOCALE_USER_DEFAULT,\r
255             DATE_SHORTDATE,\r
256             &st_dl,\r
257             NULL,\r
258             buffer,\r
259             (int) cchsize);\r
260 \r
261         StringCchCat(buffer, cchsize, L" ");\r
262 \r
263         StringCchLength(buffer, cchsize, &cch_buf_len);\r
264 \r
265         bufend = buffer + cch_buf_len;\r
266         cchsize -= cch_buf_len;\r
267     } else {\r
268         bufend = buffer;\r
269     }\r
270 \r
271     GetTimeFormat(\r
272         LOCALE_USER_DEFAULT,\r
273         0,\r
274         &st_dl,\r
275         NULL,\r
276         bufend,\r
277         (int) cchsize);\r
278 \r
279     *cb_buf = cbsize;\r
280 \r
281     return KHM_ERROR_SUCCESS;\r
282 }\r
283 \r
284 khm_boolean KHMAPI kcdb_type_date_isValid(\r
285     const void * d,\r
286     khm_size cbd)\r
287 {\r
288     return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(FILETIME)));\r
289 }\r
290 \r
291 khm_int32 KHMAPI kcdb_type_date_comp(\r
292     const void * d1,\r
293     khm_size cbd1,\r
294     const void * d2,\r
295     khm_size cbd2)\r
296 {\r
297     return (khm_int32) CompareFileTime((CONST FILETIME *) d1, (CONST FILETIME *) d2);\r
298 }\r
299 \r
300 khm_int32 KHMAPI kcdb_type_date_dup(\r
301     const void * d_src,\r
302     khm_size cbd_src,\r
303     void * d_dst,\r
304     khm_size * cbd_dst)\r
305 {\r
306     if(d_dst && *cbd_dst >= sizeof(FILETIME)) {\r
307         *cbd_dst = sizeof(FILETIME);\r
308         *((FILETIME *) d_dst) = *((FILETIME *) d_src);\r
309         return KHM_ERROR_SUCCESS;\r
310     } else {\r
311         *cbd_dst = sizeof(FILETIME);\r
312         return KHM_ERROR_TOO_LONG;\r
313     }\r
314 }\r
315 \r
316 /* Interval */\r
317 \r
318 /* returns the number of milliseconds that must elapse away from the\r
319    interval specified in pft for the representation of pft to change\r
320    from whatever it is right now */\r
321 KHMEXP long KHMAPI FtIntervalMsToRepChange(LPFILETIME pft)\r
322 {\r
323     __int64 ms,s,m,h,d;\r
324     long l;\r
325 \r
326     ms = *((__int64 *) pft) / 10000i64;\r
327     \r
328     if(ms < 0 || *((__int64 *) pft) == _I64_MAX)\r
329         return -1;\r
330 \r
331     s = ms / 1000i64;\r
332     m = s / 60;\r
333     h = s / 3600;\r
334     d = s / (3600*24);\r
335 \r
336     if(d > 0) {\r
337         /* rep change at next hour change */\r
338         l = (long) (ms % (3600*1000i64));\r
339     } else if(h > 0) {\r
340         /* rep change at next minute change */\r
341         l = (long) (ms % (60*1000i64));\r
342     } else {\r
343         l = (long) (ms % 1000);\r
344     }\r
345 \r
346     return l;\r
347 }\r
348 \r
349 KHMEXP khm_int32 KHMAPI FtIntervalToString(LPFILETIME data, wchar_t * buffer, khm_size * cb_buf)\r
350 {\r
351     size_t cbsize;\r
352     __int64 s,m,h,d;\r
353     wchar_t ibuf[256];\r
354     wchar_t fbuf[256];\r
355     wchar_t * t;\r
356 \r
357     if(!cb_buf)\r
358         return KHM_ERROR_INVALID_PARM;\r
359     s = *((__int64 *) data) / 10000000i64;\r
360 \r
361     m = s / 60;\r
362     h = s / 3600;\r
363     d = s / (3600*24);\r
364 \r
365     if(*((__int64 *) data) == _I64_MAX) {\r
366         LoadString(hinst_kcreddb, IDS_IVL_UNKNOWN, ibuf, sizeof(ibuf)/sizeof(wchar_t));\r
367     } else if(s < 0) {\r
368         LoadString(hinst_kcreddb, IDS_IVL_EXPIRED, ibuf, sizeof(ibuf)/sizeof(wchar_t));\r
369     } else if(d > 0) {\r
370         h = (s - (d * 3600 * 24)) / 3600;\r
371         if(d == 1) {\r
372             LoadString(hinst_kcreddb, IDS_IVL_1D, ibuf, ARRAYLENGTH(ibuf));\r
373         } else {\r
374             LoadString(hinst_kcreddb, IDS_IVL_D, fbuf, ARRAYLENGTH(fbuf));\r
375             StringCbPrintf(ibuf, sizeof(ibuf), fbuf, d);\r
376         }\r
377         if(h > 0) {\r
378             StringCbCat(ibuf, sizeof(ibuf), L" ");\r
379             t = ibuf + wcslen(ibuf);\r
380             if(h == 1)\r
381             {\r
382                 LoadString(hinst_kcreddb, IDS_IVL_1H, t, ARRAYLENGTH(ibuf) - wcslen(ibuf));\r
383             } else {\r
384                 LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf));\r
385                 StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, h);\r
386             }\r
387         }\r
388     } else if(h > 0) {\r
389         m = (s - (h * 3600)) / 60;\r
390         if(h == 1) {\r
391             LoadString(hinst_kcreddb, IDS_IVL_1H, ibuf, ARRAYLENGTH(ibuf));\r
392         } else {\r
393             LoadString(hinst_kcreddb, IDS_IVL_H, fbuf, ARRAYLENGTH(fbuf));\r
394             StringCbPrintf(ibuf, sizeof(ibuf), fbuf, h);\r
395         }\r
396         if(m > 0) {\r
397             StringCbCat(ibuf, sizeof(ibuf), L" ");\r
398             t = ibuf + wcslen(ibuf);\r
399             if(m == 1)\r
400             {\r
401                 LoadString(hinst_kcreddb, IDS_IVL_1M, t, ARRAYLENGTH(ibuf) - wcslen(ibuf));\r
402             } else {\r
403                 LoadString(hinst_kcreddb, IDS_IVL_M, fbuf, ARRAYLENGTH(fbuf));\r
404                 StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, m);\r
405             }\r
406         }\r
407     } else if(m > 0) {\r
408         s -= m * 60;\r
409         if(m == 1) {\r
410             LoadString(hinst_kcreddb, IDS_IVL_1M, ibuf, ARRAYLENGTH(ibuf));\r
411         } else {\r
412             LoadString(hinst_kcreddb, IDS_IVL_M, fbuf, ARRAYLENGTH(fbuf));\r
413             StringCbPrintf(ibuf, sizeof(ibuf), fbuf, m);\r
414         }\r
415         if(s > 0) {\r
416             StringCbCat(ibuf, sizeof(ibuf), L" ");\r
417             t = ibuf + wcslen(ibuf);\r
418             if(s == 1)\r
419             {\r
420                 LoadString(hinst_kcreddb, IDS_IVL_1S, t, ARRAYLENGTH(ibuf) - wcslen(ibuf));\r
421             } else {\r
422                 LoadString(hinst_kcreddb, IDS_IVL_S, fbuf, ARRAYLENGTH(fbuf));\r
423                 StringCbPrintf(t, sizeof(ibuf) - wcslen(ibuf)*sizeof(wchar_t), fbuf, s);\r
424             }\r
425         }\r
426     } else {\r
427         if(s == 1) {\r
428             LoadString(hinst_kcreddb, IDS_IVL_1S, ibuf, ARRAYLENGTH(ibuf));\r
429         } else {\r
430             LoadString(hinst_kcreddb, IDS_IVL_S, fbuf, sizeof(fbuf)/sizeof(wchar_t));\r
431             StringCbPrintf(ibuf, sizeof(ibuf), fbuf, s);\r
432         }\r
433     }\r
434 \r
435     StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
436     cbsize += sizeof(wchar_t);\r
437 \r
438     if(!buffer || *cb_buf < cbsize) {\r
439         *cb_buf = cbsize;\r
440         return KHM_ERROR_TOO_LONG;\r
441     }\r
442 \r
443     StringCbCopy(buffer, *cb_buf, ibuf);\r
444     *cb_buf = cbsize;\r
445 \r
446     return KHM_ERROR_SUCCESS;\r
447 }\r
448 \r
449 khm_int32 KHMAPI kcdb_type_interval_toString(\r
450     const void * data, \r
451     khm_size cbd, \r
452     wchar_t * buffer, \r
453     khm_size * cb_buf, \r
454     khm_int32 flags)\r
455 {\r
456     return FtIntervalToString((LPFILETIME) data, buffer, cb_buf);\r
457 }\r
458 \r
459 khm_boolean KHMAPI kcdb_type_interval_isValid(\r
460     const void * d,\r
461     khm_size cbd)\r
462 {\r
463     return (d && (cbd == sizeof(FILETIME) || cbd == KCDB_CBSIZE_AUTO));\r
464 }\r
465 \r
466 khm_int32 KHMAPI kcdb_type_interval_comp(\r
467     const void * d1,\r
468     khm_size cbd1,\r
469     const void * d2,\r
470     khm_size cbd2)\r
471 {\r
472     __int64 i1, i2;\r
473 \r
474     i1 = *((__int64 *) d1);\r
475     i2 = *((__int64 *) d2);\r
476 \r
477     if(i1 < i2)\r
478         return -1;\r
479     else if(i1 > i2)\r
480         return 1;\r
481     else\r
482         return 0;\r
483 }\r
484 \r
485 khm_int32 KHMAPI kcdb_type_interval_dup(\r
486     const void * d_src,\r
487     khm_size cbd_src,\r
488     void * d_dst,\r
489     khm_size * cbd_dst)\r
490 {\r
491     if(d_dst && *cbd_dst >= sizeof(__int64)) {\r
492         *cbd_dst = sizeof(__int64);\r
493         *((__int64 *) d_dst) = *((__int64 *) d_src);\r
494         return KHM_ERROR_SUCCESS;\r
495     } else {\r
496         *cbd_dst = sizeof(__int64);\r
497         return KHM_ERROR_TOO_LONG;\r
498     }\r
499 }\r
500 \r
501 /* Int32 */\r
502 \r
503 khm_int32 KHMAPI kcdb_type_int32_toString(\r
504     const void * d, \r
505     khm_size cbd, \r
506     wchar_t * buffer, \r
507     khm_size * cb_buf, \r
508     khm_int32 flags)\r
509 {\r
510     size_t cbsize;\r
511     wchar_t ibuf[12];\r
512 \r
513     if(!cb_buf)\r
514         return KHM_ERROR_INVALID_PARM;\r
515 \r
516     StringCbPrintf(ibuf, sizeof(ibuf), L"%d", *((khm_int32 *) d));\r
517     StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
518     cbsize += sizeof(wchar_t);\r
519 \r
520     if(!buffer || *cb_buf < cbsize) {\r
521         *cb_buf = cbsize;\r
522         return KHM_ERROR_TOO_LONG;\r
523     }\r
524 \r
525     StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf);\r
526     *cb_buf = cbsize;\r
527 \r
528     return KHM_ERROR_SUCCESS;\r
529 }\r
530 \r
531 khm_boolean KHMAPI kcdb_type_int32_isValid(\r
532     const void * d,\r
533     khm_size cbd)\r
534 {\r
535     return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(khm_int32)));\r
536 }\r
537 \r
538 khm_int32 KHMAPI kcdb_type_int32_comp(\r
539     const void * d1,\r
540     khm_size cbd1,\r
541     const void * d2,\r
542     khm_size cbd2)\r
543 {\r
544     return *((khm_int32 *) d1) - *((khm_int32 *) d2);\r
545 }\r
546 \r
547 khm_int32 KHMAPI kcdb_type_int32_dup(\r
548     const void * d_src,\r
549     khm_size cbd_src,\r
550     void * d_dst,\r
551     khm_size * cbd_dst)\r
552 {\r
553     if(d_dst && (*cbd_dst >= sizeof(khm_int32))) {\r
554         *cbd_dst = sizeof(khm_int32);\r
555         *((khm_int32 *) d_dst) = *((khm_int32 *) d_src);\r
556         return KHM_ERROR_SUCCESS;\r
557     } else {\r
558         *cbd_dst = sizeof(khm_int32);\r
559         return KHM_ERROR_TOO_LONG;\r
560     }\r
561 }\r
562 \r
563 /* Int64 */\r
564 \r
565 khm_int32 KHMAPI kcdb_type_int64_toString(\r
566     const void * d, \r
567     khm_size cbd, \r
568     wchar_t * buffer, \r
569     khm_size * cb_buf, \r
570     khm_int32 flags)\r
571 {\r
572     size_t cbsize;\r
573     wchar_t ibuf[22];\r
574 \r
575     if(!cb_buf)\r
576         return KHM_ERROR_INVALID_PARM;\r
577 \r
578     StringCbPrintf(ibuf, sizeof(ibuf), L"%I64d", *((__int64 *) d));\r
579     StringCbLength(ibuf, sizeof(ibuf), &cbsize);\r
580     cbsize += sizeof(wchar_t);\r
581 \r
582     if(!buffer || *cb_buf < cbsize) {\r
583         *cb_buf = cbsize;\r
584         return KHM_ERROR_TOO_LONG;\r
585     }\r
586 \r
587     StringCbCopy((wchar_t *) buffer, *cb_buf, ibuf);\r
588     *cb_buf = cbsize;\r
589 \r
590     return KHM_ERROR_SUCCESS;\r
591 }\r
592 \r
593 khm_boolean KHMAPI kcdb_type_int64_isValid(\r
594     const void * d,\r
595     khm_size cbd)\r
596 {\r
597     return (d && (cbd == KCDB_CBSIZE_AUTO || cbd == sizeof(__int64)));\r
598 }\r
599 \r
600 khm_int32 KHMAPI kcdb_type_int64_comp(\r
601     const void * d1,\r
602     khm_size cbd1,\r
603     const void * d2,\r
604     khm_size cbd2)\r
605 {\r
606     __int64 r = *((__int64 *) d1) - *((__int64 *) d2);\r
607     return (r==0i64)?0:((r>0i64)?1:-1);\r
608 }\r
609 \r
610 khm_int32 KHMAPI kcdb_type_int64_dup(\r
611     const void * d_src,\r
612     khm_size cbd_src,\r
613     void * d_dst,\r
614     khm_size * cbd_dst)\r
615 {\r
616     if(d_dst && (*cbd_dst >= sizeof(__int64))) {\r
617         *cbd_dst = sizeof(__int64);\r
618         *((__int64 *) d_dst) = *((__int64 *) d_src);\r
619         return KHM_ERROR_SUCCESS;\r
620     } else {\r
621         *cbd_dst = sizeof(__int64);\r
622         return KHM_ERROR_TOO_LONG;\r
623     }\r
624 }\r
625 \r
626 /* Data */\r
627 #define GENERIC_DATA_STR L"(Data)"\r
628 \r
629 khm_int32 KHMAPI kcdb_type_data_toString(\r
630     const void * d, \r
631     khm_size cbd, \r
632     wchar_t * buffer, \r
633     khm_size * cb_buf, \r
634     khm_int32 flags)\r
635 {\r
636     size_t cbsize;\r
637 \r
638     if(!cb_buf)\r
639         return KHM_ERROR_INVALID_PARM;\r
640 \r
641     cbsize = sizeof(GENERIC_DATA_STR);\r
642 \r
643     if(!buffer || *cb_buf < cbsize) {\r
644         *cb_buf = cbsize;\r
645         return KHM_ERROR_TOO_LONG;\r
646     }\r
647 \r
648     StringCbCopy(buffer, *cb_buf, GENERIC_DATA_STR);\r
649 \r
650     *cb_buf = cbsize;\r
651 \r
652     return KHM_ERROR_SUCCESS;\r
653 }\r
654 \r
655 khm_boolean KHMAPI kcdb_type_data_isValid(\r
656     const void * d,\r
657     khm_size cbd)\r
658 {\r
659     /* data is always valid, even if d is NULL */\r
660     return TRUE;\r
661 }\r
662 \r
663 khm_int32 KHMAPI kcdb_type_data_comp(\r
664     const void * d1,\r
665     khm_size cbd1,\r
666     const void * d2,\r
667     khm_size cbd2)\r
668 {\r
669     /* datas can not be compared */\r
670     return 0;\r
671 }\r
672 \r
673 khm_int32 KHMAPI kcdb_type_data_dup(\r
674     const void * d_src,\r
675     khm_size cbd_src,\r
676     void * d_dst,\r
677     khm_size * cbd_dst)\r
678 {\r
679     if(!cbd_dst)\r
680         return KHM_ERROR_INVALID_PARM;\r
681 \r
682     *cbd_dst = cbd_src;\r
683 \r
684     if(!d_dst || *cbd_dst < cbd_src) {\r
685         return KHM_ERROR_TOO_LONG;\r
686     } else {\r
687         memcpy(d_dst, d_src, cbd_src);\r
688         return KHM_ERROR_SUCCESS;\r
689     }\r
690 }\r
691 \r
692 \r
693 void kcdb_type_msg_completion(kmq_message * m) \r
694 {\r
695     kcdb_type_release((kcdb_type_i *) m->vparam);\r
696 }\r
697 \r
698 void kcdb_type_post_message(khm_int32 op, kcdb_type_i * t)\r
699 {\r
700     kcdb_type_hold(t);\r
701     kmq_post_message(KMSG_KCDB, KMSG_KCDB_TYPE, op, (void *) t);\r
702 }\r
703 \r
704 void kcdb_type_init(void)\r
705 {\r
706     kcdb_type type;\r
707 \r
708     InitializeCriticalSection(&cs_type);\r
709     kcdb_type_namemap = hash_new_hashtable(\r
710         KCDB_TYPE_HASH_SIZE,\r
711         hash_string,\r
712         hash_string_comp,\r
713         kcdb_type_add_ref,\r
714         kcdb_type_del_ref);\r
715     kcdb_type_tbl = malloc(sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1));\r
716     ZeroMemory(kcdb_type_tbl, sizeof(kcdb_type_i *) * (KCDB_TYPE_MAX_ID + 1));\r
717     kcdb_types = NULL;\r
718 \r
719     /*TODO: register standard data types */\r
720 \r
721     ZeroMemory(&type, sizeof(type));\r
722     type.comp = kcdb_type_void_comp;\r
723     type.dup = kcdb_type_void_dup;\r
724     type.isValid = kcdb_type_void_isValid;\r
725     type.toString = kcdb_type_void_toString;\r
726     type.name = KCDB_TYPENAME_VOID;\r
727     type.id = KCDB_TYPE_VOID;\r
728 \r
729     kcdb_type_register(&type, NULL);\r
730 \r
731     ZeroMemory(&type, sizeof(type));\r
732     type.comp = kcdb_type_string_comp;\r
733     type.dup = kcdb_type_string_dup;\r
734     type.isValid = kcdb_type_string_isValid;\r
735     type.toString = kcdb_type_string_toString;\r
736     type.name = KCDB_TYPENAME_STRING;\r
737     type.id = KCDB_TYPE_STRING;\r
738     type.flags = KCDB_TYPE_FLAG_CB_AUTO;\r
739 \r
740     kcdb_type_register(&type, NULL);\r
741 \r
742     ZeroMemory(&type, sizeof(type));\r
743     type.comp = kcdb_type_date_comp;\r
744     type.dup = kcdb_type_date_dup;\r
745     type.isValid = kcdb_type_date_isValid;\r
746     type.toString = kcdb_type_date_toString;\r
747     type.name = KCDB_TYPENAME_DATE;\r
748     type.id = KCDB_TYPE_DATE;\r
749     type.cb_max = sizeof(FILETIME);\r
750     type.cb_min = sizeof(FILETIME);\r
751     type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
752 \r
753     kcdb_type_register(&type, NULL);\r
754 \r
755     ZeroMemory(&type, sizeof(type));\r
756     type.comp = kcdb_type_interval_comp;\r
757     type.dup = kcdb_type_interval_dup;\r
758     type.isValid = kcdb_type_interval_isValid;\r
759     type.toString = kcdb_type_interval_toString;\r
760     type.name = KCDB_TYPENAME_INTERVAL;\r
761     type.id = KCDB_TYPE_INTERVAL;\r
762     type.cb_max = sizeof(__int64);\r
763     type.cb_min = sizeof(__int64);\r
764     type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
765 \r
766     kcdb_type_register(&type, NULL);\r
767 \r
768     ZeroMemory(&type, sizeof(type));\r
769     type.comp = kcdb_type_int32_comp;\r
770     type.dup = kcdb_type_int32_dup;\r
771     type.isValid = kcdb_type_int32_isValid;\r
772     type.toString = kcdb_type_int32_toString;\r
773     type.name = KCDB_TYPENAME_INT32;\r
774     type.id = KCDB_TYPE_INT32;\r
775     type.cb_max = sizeof(khm_int32);\r
776     type.cb_min = sizeof(khm_int32);\r
777     type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
778 \r
779     kcdb_type_register(&type, NULL);\r
780 \r
781     ZeroMemory(&type, sizeof(type));\r
782     type.comp = kcdb_type_int64_comp;\r
783     type.dup = kcdb_type_int64_dup;\r
784     type.isValid = kcdb_type_int64_isValid;\r
785     type.toString = kcdb_type_int64_toString;\r
786     type.name = KCDB_TYPENAME_INT64;\r
787     type.id = KCDB_TYPE_INT64;\r
788     type.cb_max = sizeof(__int64);\r
789     type.cb_min = sizeof(__int64);\r
790     type.flags = KCDB_TYPE_FLAG_CB_FIXED;\r
791 \r
792     kcdb_type_register(&type, NULL);\r
793 \r
794     ZeroMemory(&type, sizeof(type));\r
795     type.comp = kcdb_type_data_comp;\r
796     type.dup = kcdb_type_data_dup;\r
797     type.isValid = kcdb_type_data_isValid;\r
798     type.toString = kcdb_type_data_toString;\r
799     type.name = KCDB_TYPENAME_DATA;\r
800     type.id = KCDB_TYPE_DATA;\r
801 \r
802     kcdb_type_register(&type, NULL);\r
803 }\r
804 \r
805 void kcdb_type_add_ref(const void *key, void *vt)\r
806 {\r
807     kcdb_type_hold((kcdb_type_i *) vt);\r
808 }\r
809 \r
810 void kcdb_type_del_ref(const void *key, void *vt)\r
811 {\r
812     kcdb_type_release((kcdb_type_i *) vt);\r
813 }\r
814 \r
815 khm_int32 kcdb_type_hold(kcdb_type_i * t)\r
816 {\r
817     if(!t)\r
818         return KHM_ERROR_INVALID_PARM;\r
819 \r
820     EnterCriticalSection(&cs_type);\r
821     t->refcount++;\r
822     LeaveCriticalSection(&cs_type);\r
823 \r
824     return KHM_ERROR_SUCCESS;\r
825 }\r
826 \r
827 khm_int32 kcdb_type_release(kcdb_type_i * t)\r
828 {\r
829     if(!t)\r
830         return KHM_ERROR_INVALID_PARM;\r
831 \r
832     EnterCriticalSection(&cs_type);\r
833     t->refcount--;\r
834     kcdb_type_check_and_delete(t->type.id);\r
835     LeaveCriticalSection(&cs_type);\r
836 \r
837     return KHM_ERROR_SUCCESS;\r
838 }\r
839 \r
840 void kcdb_type_exit(void)\r
841 {\r
842     EnterCriticalSection(&cs_type);\r
843     free(kcdb_type_tbl);\r
844     /*TODO: free up the individual types */\r
845     LeaveCriticalSection(&cs_type);\r
846     DeleteCriticalSection(&cs_type);\r
847 }\r
848 \r
849 void kcdb_type_check_and_delete(khm_int32 id)\r
850 {\r
851     kcdb_type_i * t;\r
852 \r
853     if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
854         return;\r
855 \r
856     EnterCriticalSection(&cs_type);\r
857     t = kcdb_type_tbl[id];\r
858     if(t && !t->refcount) {\r
859         kcdb_type_tbl[id] = NULL;\r
860         LDELETE(&kcdb_types, t);\r
861         /* must already be out of the hash-table, otherwise refcount should not\r
862             be zero */\r
863         free(t->type.name);\r
864         free(t);\r
865     }\r
866     LeaveCriticalSection(&cs_type);\r
867 }\r
868 \r
869 KHMEXP khm_int32 KHMAPI kcdb_type_get_id(wchar_t *name, khm_int32 * id)\r
870 {\r
871     kcdb_type_i * t;\r
872     size_t cbsize;\r
873 \r
874     if(FAILED(StringCbLength(name, KCDB_MAXCB_NAME, &cbsize))) {\r
875         /* also fails of name is NULL */\r
876         return KHM_ERROR_INVALID_PARM;\r
877     }\r
878 \r
879     EnterCriticalSection(&cs_type);\r
880     t = hash_lookup(kcdb_type_namemap, (void*) name);\r
881     LeaveCriticalSection(&cs_type);\r
882 \r
883     if(!t) {\r
884         *id = KCDB_TYPE_INVALID;\r
885         return KHM_ERROR_NOT_FOUND;\r
886     } else {\r
887         *id = t->type.id;\r
888         return KHM_ERROR_SUCCESS;\r
889     }\r
890 }\r
891 \r
892 KHMEXP khm_int32 KHMAPI kcdb_type_get_info(khm_int32 id, kcdb_type ** info)\r
893 {\r
894     kcdb_type_i * t;\r
895 \r
896     if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
897         return KHM_ERROR_INVALID_PARM;\r
898 \r
899     EnterCriticalSection(&cs_type);\r
900     t = kcdb_type_tbl[id];\r
901 \r
902     if (t)\r
903         kcdb_type_hold(t);\r
904     LeaveCriticalSection(&cs_type);\r
905 \r
906     if(info)\r
907         *info = (kcdb_type *) t;\r
908     else if (t)\r
909         kcdb_type_release(t);\r
910 \r
911     return (t)? KHM_ERROR_SUCCESS : KHM_ERROR_NOT_FOUND;\r
912 }\r
913 \r
914 KHMEXP khm_int32 KHMAPI kcdb_type_release_info(kcdb_type * info)\r
915 {\r
916     return kcdb_type_release((kcdb_type_i *) info);\r
917 }\r
918 \r
919 KHMEXP khm_int32 KHMAPI kcdb_type_get_name(khm_int32 id, wchar_t * buffer, khm_size * cbbuf)\r
920 {\r
921     size_t cbsize;\r
922     kcdb_type_i * t;\r
923 \r
924     if(id < 0 || id > KCDB_TYPE_MAX_ID || !cbbuf)\r
925         return KHM_ERROR_INVALID_PARM;\r
926 \r
927     t = kcdb_type_tbl[id];\r
928 \r
929     if(!t)\r
930         return KHM_ERROR_NOT_FOUND;\r
931 \r
932     if(FAILED(StringCbLength(t->type.name, KCDB_MAXCB_NAME, &cbsize)))\r
933         return KHM_ERROR_UNKNOWN;\r
934 \r
935     cbsize += sizeof(wchar_t);\r
936 \r
937     if(!buffer || *cbbuf < cbsize) {\r
938         *cbbuf = cbsize;\r
939         return KHM_ERROR_TOO_LONG;\r
940     }\r
941 \r
942     StringCbCopy(buffer, *cbbuf, t->type.name);\r
943     *cbbuf = cbsize;\r
944 \r
945     return KHM_ERROR_SUCCESS;\r
946 }\r
947 \r
948 KHMEXP khm_int32 KHMAPI kcdb_type_register(kcdb_type * type, khm_int32 * new_id)\r
949 {\r
950     kcdb_type_i *t;\r
951     size_t cbsize;\r
952     khm_int32 type_id;\r
953 \r
954     if(!type || \r
955         !type->comp || \r
956         !type->dup || \r
957         !type->isValid || \r
958         !type->toString || \r
959         !type->name)\r
960         return KHM_ERROR_INVALID_PARM;\r
961 \r
962     if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&\r
963         (type->cb_min < 0 || type->cb_min > KCDB_TYPE_MAXCB))\r
964     {\r
965         return KHM_ERROR_INVALID_PARM;\r
966     }\r
967 \r
968     if((type->flags & KCDB_TYPE_FLAG_CB_MAX) &&\r
969         (type->cb_max < 0 || type->cb_max > KCDB_TYPE_MAXCB))\r
970     {\r
971         return KHM_ERROR_INVALID_PARM;\r
972     }\r
973 \r
974     if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&\r
975         (type->flags & KCDB_TYPE_FLAG_CB_MAX) &&\r
976         (type->cb_max < type->cb_min))\r
977     {\r
978         return KHM_ERROR_INVALID_PARM;\r
979     }\r
980 \r
981     if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cbsize)))\r
982         return KHM_ERROR_TOO_LONG;\r
983 \r
984     cbsize += sizeof(wchar_t);\r
985 \r
986     EnterCriticalSection(&cs_type);\r
987     if(type->id == KCDB_TYPE_INVALID) {\r
988         kcdb_type_get_next_free(&type_id);\r
989     } else if(type->id < 0 || type->id > KCDB_TYPE_MAX_ID) {\r
990         LeaveCriticalSection(&cs_type);\r
991         return KHM_ERROR_INVALID_PARM;\r
992     } else if(kcdb_type_tbl[type->id]) {\r
993         LeaveCriticalSection(&cs_type);\r
994         return KHM_ERROR_DUPLICATE;\r
995     } else {\r
996         type_id = type->id;\r
997     }\r
998 \r
999     if(type_id == KCDB_TYPE_INVALID) {\r
1000         LeaveCriticalSection(&cs_type);\r
1001         return KHM_ERROR_NO_RESOURCES;\r
1002     }\r
1003 \r
1004     t = malloc(sizeof(kcdb_type_i));\r
1005     ZeroMemory(t, sizeof(kcdb_type_i));\r
1006 \r
1007     t->type.name = malloc(cbsize);\r
1008     StringCbCopy(t->type.name, cbsize, type->name);\r
1009 \r
1010     t->type.comp = type->comp;\r
1011     t->type.dup = type->dup;\r
1012     t->type.flags = type->flags;\r
1013     t->type.id = type_id;\r
1014     t->type.isValid = type->isValid;\r
1015     t->type.toString = type->toString;\r
1016 \r
1017     LINIT(t);\r
1018 \r
1019     kcdb_type_tbl[type_id] = t;\r
1020     LPUSH(&kcdb_types, t);\r
1021 \r
1022     hash_add(kcdb_type_namemap, (void *) t->type.name, (void *) t);\r
1023 \r
1024     LeaveCriticalSection(&cs_type);\r
1025 \r
1026     if(new_id)\r
1027         *new_id = type_id;\r
1028 \r
1029     kcdb_type_post_message(KCDB_OP_INSERT, t);\r
1030 \r
1031     return KHM_ERROR_SUCCESS;\r
1032 }\r
1033 \r
1034 KHMEXP khm_int32 KHMAPI kcdb_type_unregister(khm_int32 id)\r
1035 {\r
1036     kcdb_type_i * t;\r
1037 \r
1038     if(id < 0 || id > KCDB_TYPE_MAX_ID)\r
1039         return KHM_ERROR_INVALID_PARM;\r
1040 \r
1041     EnterCriticalSection(&cs_type);\r
1042     t = kcdb_type_tbl[id];\r
1043     if(t) {\r
1044         kcdb_type_post_message(KCDB_OP_DELETE, t);\r
1045         /* we are going to remove t from the hash table.  If no one is holding\r
1046             a reference to it, then we can free it (actually, the del_ref code\r
1047             will take care of that anyway).  If there is a hold, then it will\r
1048             get freed when they release it. \r
1049             \r
1050             Actually, the post_message call above pretty much guarantees that\r
1051             the type has a hold on it.*/\r
1052         t->type.flags |= KCDB_TYPE_FLAG_DELETED;\r
1053         hash_del(kcdb_type_namemap, t->type.name);\r
1054     }\r
1055     LeaveCriticalSection(&cs_type);\r
1056 \r
1057     if(t)\r
1058         return KHM_ERROR_SUCCESS;\r
1059     else\r
1060         return KHM_ERROR_NOT_FOUND;\r
1061 }\r
1062 \r
1063 KHMEXP khm_int32 KHMAPI kcdb_type_get_next_free(khm_int32 * id)\r
1064 {\r
1065     int i;\r
1066 \r
1067     if(!id)\r
1068         return KHM_ERROR_INVALID_PARM;\r
1069 \r
1070     /* do a linear search because this function only gets called a few times */\r
1071     EnterCriticalSection(&cs_type);\r
1072     for(i=0; i <= KCDB_TYPE_MAX_ID; i++) {\r
1073         if(!kcdb_type_tbl[i])\r
1074             break;\r
1075     }\r
1076     LeaveCriticalSection(&cs_type);\r
1077 \r
1078     if(i <= KCDB_TYPE_MAX_ID) {\r
1079         *id = i;\r
1080         return KHM_ERROR_SUCCESS;\r
1081     } else {\r
1082         *id = KCDB_TYPE_INVALID;\r
1083         return KHM_ERROR_NO_RESOURCES;\r
1084     }\r
1085 }\r
1086 \r
1087 /* Conversion functions */\r
1088 \r
1089 KHMEXP void KHMAPI TimetToFileTime( time_t t, LPFILETIME pft )\r
1090 {\r
1091     LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000i64;\r
1092     pft->dwLowDateTime = (DWORD) ll;\r
1093     pft->dwHighDateTime = (DWORD) (ll >> 32);\r
1094 }\r
1095 \r
1096 KHMEXP void KHMAPI TimetToFileTimeInterval(time_t t, LPFILETIME pft)\r
1097 {\r
1098     LONGLONG ll = Int32x32To64(t, 10000000);\r
1099     pft->dwLowDateTime = (DWORD) ll;\r
1100     pft->dwHighDateTime = (DWORD) (ll >> 32);\r
1101 }\r
1102 \r
1103 KHMEXP long KHMAPI FtIntervalToSeconds(LPFILETIME pft)\r
1104 {\r
1105     __int64 i = *((__int64 *) pft);\r
1106     return (long) (i / 10000000i64);\r
1107 }\r
1108 \r
1109 KHMEXP long KHMAPI FtIntervalToMilliseconds(LPFILETIME pft)\r
1110 {\r
1111     __int64 i = *((__int64 *) pft);\r
1112     return (long) (i / 10000i64);\r
1113 }\r
1114 \r
1115 KHMEXP long KHMAPI FtCompare(LPFILETIME pft1, LPFILETIME pft2) {\r
1116     __int64 i1 = *((__int64 *) pft1);\r
1117     __int64 i2 = *((__int64 *) pft2);\r
1118 \r
1119     if (i1 < i2)\r
1120         return -1;\r
1121     if (i1 == i2)\r
1122         return 0;\r
1123     return 1;\r
1124 }\r
1125 \r
1126 KHMEXP int KHMAPI AnsiStrToUnicode( wchar_t * wstr, size_t cbwstr, const char * astr)\r
1127 {\r
1128     size_t nc;\r
1129 \r
1130     if(cbwstr == 0)\r
1131         return 0;\r
1132 \r
1133     nc = strlen(astr);\r
1134     if(nc == MultiByteToWideChar(\r
1135         CP_ACP, \r
1136         0, \r
1137         astr, \r
1138         (int) nc, \r
1139         wstr, \r
1140         (int)(cbwstr / sizeof(wchar_t) - 1))) {\r
1141         wstr[nc] = L'\0';\r
1142     } else {\r
1143         wstr[0] = L'\0';\r
1144         nc = 0;\r
1145     }\r
1146 \r
1147     return (int) nc;\r
1148 }\r
1149 \r
1150 KHMEXP int KHMAPI UnicodeStrToAnsi( char * dest, size_t cbdest, const wchar_t * src)\r
1151 {\r
1152     size_t nc;\r
1153 \r
1154     if(cbdest == 0)\r
1155         return 0;\r
1156 \r
1157     dest[0] = 0;\r
1158 \r
1159     if(FAILED(StringCchLength(src, cbdest, &nc)) || nc*sizeof(char) >= cbdest)\r
1160         // note that cbdest counts the terminating NULL, while nc doesn't\r
1161         return 0;\r
1162 \r
1163     nc = WideCharToMultiByte(\r
1164         CP_ACP, \r
1165         WC_NO_BEST_FIT_CHARS, \r
1166         src, \r
1167         (int) nc, \r
1168         dest, \r
1169         (int) cbdest, \r
1170         NULL, \r
1171         NULL);\r
1172 \r
1173     dest[nc] = 0;\r
1174 \r
1175     return (int) nc;\r
1176 }\r
1177 \r
1178 #define MAX_IVL_SPECLIST_LEN 256\r
1179 #define MAX_IVL_UNITS 5\r
1180 \r
1181 enum _ivl_indices {\r
1182     IVL_SECONDS = 0,\r
1183     IVL_MINUTES,\r
1184     IVL_HOURS,\r
1185     IVL_DAYS,\r
1186     IVL_WEEKS\r
1187 };\r
1188 \r
1189 typedef struct ivspec_t {\r
1190     wchar_t str[MAX_IVL_SPECLIST_LEN];\r
1191     __int64 mul;\r
1192 } ivspec;\r
1193 \r
1194 static ivspec ivspecs[MAX_IVL_UNITS];\r
1195 static BOOL ivspecs_loaded = FALSE;\r
1196 \r
1197 int _iv_is_in_spec(wchar_t *s, int n, wchar_t * spec)\r
1198 {\r
1199     /* spec strigns are comma separated */\r
1200     wchar_t *b, *e;\r
1201 \r
1202     b = spec;\r
1203     while(*b) {\r
1204         e = wcschr(b, L',');\r
1205         if(!e)\r
1206             e = b + wcslen(b);\r
1207     \r
1208         if((e - b) == n  && !wcsnicmp(b, s, n)) {\r
1209             return TRUE;\r
1210         }\r
1211 \r
1212         if(*e)\r
1213             b = e+1;\r
1214         else\r
1215             break;\r
1216     }\r
1217 \r
1218     return FALSE;\r
1219 }\r
1220 \r
1221 KHMEXP khm_int32 KHMAPI IntervalStringToFt(FILETIME * pft, wchar_t * str)\r
1222 {\r
1223     size_t cb;\r
1224     wchar_t * b;\r
1225     __int64 *pr, t;\r
1226 \r
1227     pr = (__int64 *) pft;\r
1228     *pr = 0;\r
1229 \r
1230     /* ideally we should synchronize this, but it doesn't hurt if two\r
1231        threads do this at the same time, because we only set the ivspecs_loaded\r
1232        flag when we are done */\r
1233     if(!ivspecs_loaded) {\r
1234         LoadString(hinst_kcreddb, IDS_IVL_S_SPEC, ivspecs[IVL_SECONDS].str, MAX_IVL_SPECLIST_LEN);\r
1235         ivspecs[IVL_SECONDS].mul = 10000000i64;\r
1236         LoadString(hinst_kcreddb, IDS_IVL_M_SPEC, ivspecs[IVL_MINUTES].str, MAX_IVL_SPECLIST_LEN);\r
1237         ivspecs[IVL_MINUTES].mul = ivspecs[IVL_SECONDS].mul * 60;\r
1238         LoadString(hinst_kcreddb, IDS_IVL_H_SPEC, ivspecs[2].str, MAX_IVL_SPECLIST_LEN);\r
1239         ivspecs[IVL_HOURS].mul = ivspecs[IVL_MINUTES].mul * 60;\r
1240         LoadString(hinst_kcreddb, IDS_IVL_D_SPEC, ivspecs[3].str, MAX_IVL_SPECLIST_LEN);\r
1241         ivspecs[IVL_DAYS].mul = ivspecs[IVL_HOURS].mul * 24;\r
1242         LoadString(hinst_kcreddb, IDS_IVL_W_SPEC, ivspecs[4].str, MAX_IVL_SPECLIST_LEN);\r
1243         ivspecs[IVL_WEEKS].mul = ivspecs[IVL_DAYS].mul * 7;\r
1244 \r
1245         ivspecs_loaded = TRUE;\r
1246     }\r
1247 \r
1248     if(!str || FAILED(StringCbLength(str, MAX_IVL_SPECLIST_LEN, &cb)))\r
1249         return KHM_ERROR_INVALID_PARM;\r
1250 \r
1251     b = str;\r
1252     t = 0;\r
1253     while(*b) {\r
1254         __int64 f = 1;\r
1255         wchar_t *e;\r
1256         int i;\r
1257 \r
1258         while(*b && iswspace(*b))\r
1259             b++;\r
1260 \r
1261         if(*b && iswdigit(*b)) {\r
1262             f = _wtoi64(b);\r
1263 \r
1264             while(*b && iswdigit(*b))\r
1265                 b++;\r
1266         }\r
1267 \r
1268         while(*b && iswspace(*b))\r
1269             b++;\r
1270 \r
1271         if(!*b) /* no unit specified */\r
1272             return KHM_ERROR_INVALID_PARM;\r
1273 \r
1274         e = b;\r
1275 \r
1276         while(*e && !iswspace(*e))\r
1277             e++;\r
1278 \r
1279         for(i=0; i < MAX_IVL_UNITS; i++) {\r
1280             if(_iv_is_in_spec(b, (int)(e-b), ivspecs[i].str))\r
1281                 break;\r
1282         }\r
1283 \r
1284         if(i==MAX_IVL_UNITS)\r
1285             return KHM_ERROR_INVALID_PARM;\r
1286 \r
1287         t += f * ivspecs[i].mul;\r
1288 \r
1289         b = e;\r
1290     }\r
1291 \r
1292     *pr = t;\r
1293 \r
1294     return KHM_ERROR_SUCCESS;\r
1295 }\r