Print this page
2964 need POSIX 2008 locale object support
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Approved by: TBD
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libc/port/locale/strptime.c
+++ new/usr/src/lib/libc/port/locale/strptime.c
1 1 /*
2 2 * Copyright (c) 2014 Gary Mills
3 + * Copyright 2014 Garrett D'Amore <garrett@damore.org>
3 4 * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
4 5 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
5 6 *
6 7 * Redistribution and use in source and binary forms, with or without
7 8 * modification, are permitted provided that the following conditions
8 9 * are met:
9 10 *
10 11 * 1. Redistributions of source code must retain the above copyright
11 12 * notice, this list of conditions and the following disclaimer.
12 13 *
13 14 * 2. Redistributions in binary form must reproduce the above copyright
14 15 * notice, this list of conditions and the following disclaimer
15 16 * in the documentation and/or other materials provided with the
16 17 * distribution.
17 18 *
18 19 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
19 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
22 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 28 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 30 *
30 31 * The views and conclusions contained in the software and documentation
31 32 * are those of the authors and should not be interpreted as representing
32 33 * official policies, either expressed or implied, of Powerdog Industries.
↓ open down ↓ |
20 lines elided |
↑ open up ↑ |
33 34 */
34 35
35 36 #include "lint.h"
36 37 #include <time.h>
37 38 #include <ctype.h>
38 39 #include <errno.h>
39 40 #include <stdlib.h>
40 41 #include <string.h>
41 42 #include <pthread.h>
42 43 #include <alloca.h>
44 +#include <locale.h>
43 45 #include "timelocal.h"
46 +#include "localeimpl.h"
44 47
45 48 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
46 49
47 50 #define F_GMT (1 << 0)
48 -#define F_ZERO (1 << 1)
49 51 #define F_RECURSE (1 << 2)
50 52
51 53 static char *
52 -__strptime(const char *buf, const char *fmt, struct tm *tm, int *flagsp)
54 +__strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
55 + struct tm *_RESTRICT_KYWD tm, int *_RESTRICT_KYWD flagsp,
56 + locale_t _RESTRICT_KYWD loc)
53 57 {
54 58 char c;
55 59 const char *ptr;
56 60 int i, len, recurse = 0;
57 61 int Ealternative, Oalternative;
58 - struct lc_time_T *tptr = __get_current_time_locale();
62 + const struct lc_time *tptr = loc->time;
59 63
60 64 if (*flagsp & F_RECURSE)
61 65 recurse = 1;
62 66 *flagsp |= F_RECURSE;
63 67
64 - if (*flagsp & F_ZERO)
65 - (void) memset(tm, 0, sizeof (*tm));
66 - *flagsp &= ~F_ZERO;
67 -
68 68 ptr = fmt;
69 69 while (*ptr != 0) {
70 70 if (*buf == 0)
71 71 break;
72 72
73 73 c = *ptr++;
74 74
75 75 if (c != '%') {
76 76 if (isspace(c))
77 77 while (isspace(*buf))
78 78 buf++;
79 79 else if (c != *buf++)
80 80 return (NULL);
81 81 continue;
82 82 }
83 83
84 84 Ealternative = 0;
85 85 Oalternative = 0;
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
86 86 label:
87 87 c = *ptr++;
88 88 switch (c) {
89 89 case 0:
90 90 case '%':
91 91 if (*buf++ != '%')
92 92 return (NULL);
93 93 break;
94 94
95 95 case '+':
96 - buf = __strptime(buf, tptr->date_fmt, tm, flagsp);
96 + buf = __strptime(buf, tptr->date_fmt, tm, flagsp, loc);
97 97 if (buf == NULL)
98 98 return (NULL);
99 99 break;
100 100
101 101 case 'C':
102 102 if (!isdigit(*buf))
103 103 return (NULL);
104 104
105 105 /* XXX This will break for 3-digit centuries. */
106 106 len = 2;
107 107 for (i = 0; len && isdigit(*buf); buf++) {
108 108 i *= 10;
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
109 109 i += *buf - '0';
110 110 len--;
111 111 }
112 112 if (i < 19)
113 113 return (NULL);
114 114
115 115 tm->tm_year = i * 100 - 1900;
116 116 break;
117 117
118 118 case 'c':
119 - buf = __strptime(buf, tptr->c_fmt, tm, flagsp);
119 + buf = __strptime(buf, tptr->c_fmt, tm, flagsp, loc);
120 120 if (buf == NULL)
121 121 return (NULL);
122 122 break;
123 123
124 124 case 'D':
125 - buf = __strptime(buf, "%m/%d/%y", tm, flagsp);
125 + buf = __strptime(buf, "%m/%d/%y", tm, flagsp, loc);
126 126 if (buf == NULL)
127 127 return (NULL);
128 128 break;
129 129
130 130 case 'E':
131 131 if (Ealternative || Oalternative)
132 132 break;
133 133 Ealternative++;
134 134 goto label;
135 135
136 136 case 'O':
137 137 if (Ealternative || Oalternative)
138 138 break;
139 139 Oalternative++;
140 140 goto label;
141 141
142 142 case 'F':
143 - buf = __strptime(buf, "%Y-%m-%d", tm, flagsp);
143 + buf = __strptime(buf, "%Y-%m-%d", tm, flagsp, loc);
144 144 if (buf == NULL)
145 145 return (NULL);
146 146 break;
147 147
148 148 case 'R':
149 - buf = __strptime(buf, "%H:%M", tm, flagsp);
149 + buf = __strptime(buf, "%H:%M", tm, flagsp, loc);
150 150 if (buf == NULL)
151 151 return (NULL);
152 152 break;
153 153
154 154 case 'r':
155 - buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp);
155 + buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp, loc);
156 156 if (buf == NULL)
157 157 return (NULL);
158 158 break;
159 159
160 160 case 'T':
161 - buf = __strptime(buf, "%H:%M:%S", tm, flagsp);
161 + buf = __strptime(buf, "%H:%M:%S", tm, flagsp, loc);
162 162 if (buf == NULL)
163 163 return (NULL);
164 164 break;
165 165
166 166 case 'X':
167 - buf = __strptime(buf, tptr->X_fmt, tm, flagsp);
167 + buf = __strptime(buf, tptr->X_fmt, tm, flagsp, loc);
168 168 if (buf == NULL)
169 169 return (NULL);
170 170 break;
171 171
172 172 case 'x':
173 - buf = __strptime(buf, tptr->x_fmt, tm, flagsp);
173 + buf = __strptime(buf, tptr->x_fmt, tm, flagsp, loc);
174 174 if (buf == NULL)
175 175 return (NULL);
176 176 break;
177 177
178 178 case 'j':
179 179 if (!isdigit(*buf))
180 180 return (NULL);
181 181
182 182 len = 3;
183 183 for (i = 0; len && isdigit(*buf); buf++) {
184 184 i *= 10;
185 185 i += *buf - '0';
186 186 len--;
187 187 }
188 188 if (i < 1 || i > 366)
189 189 return (NULL);
190 190
191 191 tm->tm_yday = i - 1;
192 192 break;
193 193
194 194 case 'M':
195 195 case 'S':
196 196 if (*buf == 0 || isspace(*buf))
197 197 break;
198 198
199 199 if (!isdigit(*buf))
200 200 return (NULL);
201 201
202 202 len = 2;
203 203 for (i = 0; len && isdigit(*buf); buf++) {
204 204 i *= 10;
205 205 i += *buf - '0';
206 206 len--;
207 207 }
208 208
209 209 if (c == 'M') {
210 210 if (i > 59)
211 211 return (NULL);
212 212 tm->tm_min = i;
213 213 } else {
214 214 if (i > 60)
215 215 return (NULL);
216 216 tm->tm_sec = i;
217 217 }
218 218
219 219 break;
220 220
221 221 case 'H':
222 222 case 'I':
223 223 case 'k':
224 224 case 'l':
225 225 /*
226 226 * Of these, %l is the only specifier explicitly
227 227 * documented as not being zero-padded. However,
228 228 * there is no harm in allowing zero-padding.
229 229 *
230 230 * XXX The %l specifier may gobble one too many
231 231 * digits if used incorrectly.
232 232 */
233 233 if (!isdigit(*buf))
234 234 return (NULL);
235 235
236 236 len = 2;
237 237 for (i = 0; len && isdigit(*buf); buf++) {
238 238 i *= 10;
239 239 i += *buf - '0';
240 240 len--;
241 241 }
242 242 if (c == 'H' || c == 'k') {
243 243 if (i > 23)
244 244 return (NULL);
245 245 } else if (i > 12)
246 246 return (NULL);
247 247
248 248 tm->tm_hour = i;
249 249
250 250 break;
251 251
252 252 case 'p':
253 253 /*
254 254 * XXX This is bogus if parsed before hour-related
255 255 * specifiers.
256 256 */
257 257 len = strlen(tptr->am);
258 258 if (strncasecmp(buf, tptr->am, len) == 0) {
259 259 if (tm->tm_hour > 12)
260 260 return (NULL);
261 261 if (tm->tm_hour == 12)
262 262 tm->tm_hour = 0;
263 263 buf += len;
264 264 break;
265 265 }
266 266
267 267 len = strlen(tptr->pm);
268 268 if (strncasecmp(buf, tptr->pm, len) == 0) {
269 269 if (tm->tm_hour > 12)
270 270 return (NULL);
271 271 if (tm->tm_hour != 12)
272 272 tm->tm_hour += 12;
273 273 buf += len;
274 274 break;
275 275 }
276 276
277 277 return (NULL);
278 278
279 279 case 'A':
280 280 case 'a':
281 281 for (i = 0; i < asizeof(tptr->weekday); i++) {
282 282 len = strlen(tptr->weekday[i]);
283 283 if (strncasecmp(buf, tptr->weekday[i], len) ==
284 284 0)
285 285 break;
286 286 len = strlen(tptr->wday[i]);
287 287 if (strncasecmp(buf, tptr->wday[i], len) == 0)
288 288 break;
289 289 }
290 290 if (i == asizeof(tptr->weekday))
291 291 return (NULL);
292 292
293 293 tm->tm_wday = i;
294 294 buf += len;
295 295 break;
296 296
297 297 case 'U':
298 298 case 'W':
299 299 /*
300 300 * XXX This is bogus, as we can not assume any valid
301 301 * information present in the tm structure at this
302 302 * point to calculate a real value, so just check the
303 303 * range for now.
304 304 */
305 305 if (!isdigit(*buf))
306 306 return (NULL);
307 307
308 308 len = 2;
309 309 for (i = 0; len && isdigit(*buf); buf++) {
310 310 i *= 10;
311 311 i += *buf - '0';
312 312 len--;
313 313 }
314 314 if (i > 53)
315 315 return (NULL);
316 316
317 317 break;
318 318
319 319 case 'w':
320 320 if (!isdigit(*buf))
321 321 return (NULL);
322 322
323 323 i = *buf - '0';
324 324 if (i > 6)
325 325 return (NULL);
326 326
327 327 tm->tm_wday = i;
328 328
329 329 break;
330 330
331 331 case 'd':
332 332 case 'e':
333 333 /*
334 334 * The %e format has a space before single digits
335 335 * which we need to skip.
336 336 */
337 337 if (isspace(*buf))
338 338 buf++;
339 339 /*
340 340 * The %e specifier is explicitly documented as not
341 341 * being zero-padded but there is no harm in allowing
342 342 * such padding.
343 343 *
344 344 * XXX The %e specifier may gobble one too many
345 345 * digits if used incorrectly.
346 346 */
347 347 if (!isdigit(*buf))
348 348 return (NULL);
349 349
350 350 len = 2;
351 351 for (i = 0; len && isdigit(*buf); buf++) {
352 352 i *= 10;
353 353 i += *buf - '0';
354 354 len--;
355 355 }
356 356 if (i > 31)
357 357 return (NULL);
358 358
359 359 tm->tm_mday = i;
360 360
361 361 break;
362 362
363 363 case 'B':
364 364 case 'b':
365 365 case 'h':
366 366 for (i = 0; i < asizeof(tptr->month); i++) {
367 367 len = strlen(tptr->month[i]);
368 368 if (strncasecmp(buf, tptr->month[i], len) == 0)
369 369 break;
370 370 }
371 371 /*
372 372 * Try the abbreviated month name if the full name
373 373 * wasn't found.
374 374 */
375 375 if (i == asizeof(tptr->month)) {
376 376 for (i = 0; i < asizeof(tptr->month); i++) {
377 377 len = strlen(tptr->mon[i]);
378 378 if (strncasecmp(buf, tptr->mon[i],
379 379 len) == 0)
380 380 break;
381 381 }
382 382 }
383 383 if (i == asizeof(tptr->month))
384 384 return (NULL);
385 385
386 386 tm->tm_mon = i;
387 387 buf += len;
388 388 break;
389 389
390 390 case 'm':
391 391 if (!isdigit(*buf))
392 392 return (NULL);
393 393
394 394 len = 2;
395 395 for (i = 0; len && isdigit(*buf); buf++) {
396 396 i *= 10;
397 397 i += *buf - '0';
398 398 len--;
399 399 }
400 400 if (i < 1 || i > 12)
401 401 return (NULL);
402 402
403 403 tm->tm_mon = i - 1;
404 404
405 405 break;
406 406
407 407 case 's':
408 408 {
409 409 char *cp;
410 410 int sverrno;
411 411 time_t t;
412 412
413 413 sverrno = errno;
414 414 errno = 0;
415 415 t = strtol(buf, &cp, 10);
416 416 if (errno == ERANGE) {
417 417 errno = sverrno;
418 418 return (NULL);
419 419 }
420 420 errno = sverrno;
421 421 buf = cp;
422 422 (void) gmtime_r(&t, tm);
423 423 *flagsp |= F_GMT;
424 424 }
425 425 break;
426 426
427 427 case 'Y':
428 428 case 'y':
429 429 if (*buf == NULL || isspace(*buf))
430 430 break;
431 431
432 432 if (!isdigit(*buf))
433 433 return (NULL);
434 434
435 435 len = (c == 'Y') ? 4 : 2;
436 436 for (i = 0; len && isdigit(*buf); buf++) {
437 437 i *= 10;
438 438 i += *buf - '0';
439 439 len--;
440 440 }
441 441 if (c == 'Y')
442 442 i -= 1900;
443 443 if (c == 'y' && i < 69)
444 444 i += 100;
445 445 if (i < 0)
446 446 return (NULL);
447 447
448 448 tm->tm_year = i;
449 449
450 450 break;
451 451
452 452 case 'Z':
453 453 {
454 454 const char *cp = buf;
455 455 char *zonestr;
456 456
457 457 while (isupper(*cp))
458 458 ++cp;
459 459 if (cp - buf) {
460 460 zonestr = alloca(cp - buf + 1);
461 461 (void) strncpy(zonestr, buf, cp - buf);
462 462 zonestr[cp - buf] = '\0';
463 463 tzset();
464 464 if (strcmp(zonestr, "GMT") == 0) {
465 465 *flagsp |= F_GMT;
466 466 } else if (0 == strcmp(zonestr, tzname[0])) {
467 467 tm->tm_isdst = 0;
468 468 } else if (0 == strcmp(zonestr, tzname[1])) {
469 469 tm->tm_isdst = 1;
470 470 } else {
471 471 return (NULL);
472 472 }
473 473 buf += cp - buf;
474 474 }
475 475 }
476 476 break;
477 477
478 478 case 'z':
479 479 {
480 480 int sign = 1;
481 481
482 482 if (*buf != '+') {
483 483 if (*buf == '-')
484 484 sign = -1;
485 485 else
486 486 return (NULL);
487 487 }
488 488 buf++;
489 489 i = 0;
490 490 for (len = 4; len > 0; len--) {
491 491 if (!isdigit(*buf))
492 492 return (NULL);
493 493 i *= 10;
494 494 i += *buf - '0';
495 495 buf++;
496 496 }
497 497
498 498 tm->tm_hour -= sign * (i / 100);
499 499 tm->tm_min -= sign * (i % 100);
500 500 *flagsp |= F_GMT;
501 501 }
502 502 break;
503 503 case 'n':
504 504 case 't':
505 505 while (isspace(*buf))
506 506 buf++;
507 507 break;
508 508 }
509 509 }
510 510
511 511 if (!recurse) {
↓ open down ↓ |
328 lines elided |
↑ open up ↑ |
512 512 if (buf && (*flagsp & F_GMT)) {
513 513 time_t t = timegm(tm);
514 514 (void) localtime_r(&t, tm);
515 515 }
516 516 }
517 517
518 518 return ((char *)buf);
519 519 }
520 520
521 521 char *
522 -strptime(const char *buf, const char *fmt, struct tm *tm)
522 +strptime(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
523 + struct tm *_RESTRICT_KYWD tm)
523 524 {
524 - int flags = F_ZERO;
525 + int flags = 0;
525 526
526 - return (__strptime(buf, fmt, tm, &flags));
527 + (void) memset(tm, 0, sizeof (*tm));
528 +
529 + return (__strptime(buf, fmt, tm, &flags, uselocale(NULL)));
527 530 }
528 531
529 532 /*
530 533 * This is used by Solaris, and is a variant that does not clear the
531 534 * incoming tm. It is triggered by -D_STRPTIME_DONTZERO.
532 535 */
533 536 char *
534 -__strptime_dontzero(const char *buf, const char *fmt, struct tm *tm)
537 +__strptime_dontzero(const char *_RESTRICT_KYWD buf,
538 + const char *_RESTRICT_KYWD fmt, struct tm *_RESTRICT_KYWD tm)
535 539 {
536 540 int flags = 0;
537 541
538 - return (__strptime(buf, fmt, tm, &flags));
542 + return (__strptime(buf, fmt, tm, &flags, uselocale(NULL)));
543 +}
544 +
545 +/*
546 + * strptime_l is an extension that seems natural, and indeed, MacOS X
547 + * includes it within their <xlocale.h> and it is part of GNU libc as well.
548 + * For now we restrict it to the cases where strict namespaces are not
549 + * included. We expect to see it in a future version of POSIX. locale_t is
550 + * not a restrict, since the spec for it doesn't assume its a pointer. We
551 + * therefore pass it analagously to the way strftime_l is specified.
552 + *
553 + * We are not providing a non-zeroing version at this time.
554 + */
555 +char *
556 +strptime_l(const char *_RESTRICT_KYWD buf, const char *_RESTRICT_KYWD fmt,
557 + struct tm *_RESTRICT_KYWD tm, locale_t loc)
558 +{
559 + int flags = 0;
560 +
561 + (void) memset(tm, 0, sizeof (*tm));
562 +
563 + return (__strptime(buf, fmt, tm, &flags, loc));
539 564 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX