Print this page
2964 need POSIX 2008 locale object support
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libc/port/locale/strftime.c
+++ new/usr/src/lib/libc/port/locale/strftime.c
1 1 /*
2 2 * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
3 3 * Copyright (c) 1989 The Regents of the University of California.
4 4 * All rights reserved.
5 5 *
6 + * Copyright (c) 2011 The FreeBSD Foundation
7 + * All rights reserved.
8 + * Portions of this software were developed by David Chisnall
9 + * under sponsorship from the FreeBSD Foundation.
10 + *
6 11 * Redistribution and use in source and binary forms are permitted
7 12 * provided that the above copyright notice and this paragraph are
8 13 * duplicated in all such forms and that any documentation,
9 14 * advertising materials, and other materials related to such
10 15 * distribution and use acknowledge that the software was developed
11 16 * by the University of California, Berkeley. The name of the
12 17 * University may not be used to endorse or promote products derived
13 18 * from this software without specific prior written permission.
14 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 20 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 22 */
18 23
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
19 24 #include "lint.h"
20 25 #include "tzfile.h"
21 26 #include <fcntl.h>
22 27 #include <sys/stat.h>
23 28 #include <string.h>
24 29 #include <stdio.h>
25 30 #include "timelocal.h"
26 31
27 32 static char *_add(const char *, char *, const char *);
28 33 static char *_conv(int, const char *, char *, const char *);
29 -static char *_fmt(const char *, const struct tm *, char *, const char * const);
34 +static char *_fmt(const char *, const struct tm *, char *, const char * const,
35 + int *, locale_t);
30 36 static char *_yconv(int, int, int, int, char *, const char *);
31 37
32 38 extern char *tzname[];
33 39
34 40 #define IN_NONE 0
35 41 #define IN_SOME 1
36 42 #define IN_THIS 2
37 43 #define IN_ALL 3
38 44
39 45 #define PAD_DEFAULT 0
40 46 #define PAD_LESS 1
41 47 #define PAD_SPACE 2
42 48 #define PAD_ZERO 3
43 49
44 50 static const char *fmt_padding[][4] = {
45 51 /* DEFAULT, LESS, SPACE, ZERO */
46 52 #define PAD_FMT_MONTHDAY 0
47 53 #define PAD_FMT_HMS 0
48 54 #define PAD_FMT_CENTURY 0
49 55 #define PAD_FMT_SHORTYEAR 0
50 56 #define PAD_FMT_MONTH 0
51 57 #define PAD_FMT_WEEKOFYEAR 0
52 58 #define PAD_FMT_DAYOFMONTH 0
53 59 { "%02d", "%d", "%2d", "%02d" },
54 60 #define PAD_FMT_SDAYOFMONTH 1
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
55 61 #define PAD_FMT_SHMS 1
56 62 { "%2d", "%d", "%2d", "%02d" },
57 63 #define PAD_FMT_DAYOFYEAR 2
58 64 { "%03d", "%d", "%3d", "%03d" },
59 65 #define PAD_FMT_YEAR 3
60 66 { "%04d", "%d", "%4d", "%04d" }
61 67 };
62 68
63 69
64 70 size_t
65 -strftime(char *_RESTRICT_KYWD s, size_t maxsize,
66 - const char *_RESTRICT_KYWD format, const struct tm *_RESTRICT_KYWD t)
71 +strftime_l(char *_RESTRICT_KYWD s, size_t maxsize,
72 + const char *_RESTRICT_KYWD format, const struct tm *_RESTRICT_KYWD t,
73 + locale_t loc)
67 74 {
68 - char *p;
75 + char *p;
76 + int warn;
77 + FIX_LOCALE(loc);
69 78
70 79 tzset();
71 - p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize);
80 + warn = IN_NONE;
81 + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize,
82 + &warn, loc);
83 + /* XXX YEAR_2000 */
72 84 if (p == s + maxsize)
73 85 return (0);
74 86 *p = '\0';
75 87 return (p - s);
76 88 }
77 89
90 +size_t
91 +strftime(char *_RESTRICT_KYWD s, size_t maxsize,
92 + const char *_RESTRICT_KYWD format, const struct tm *_RESTRICT_KYWD t)
93 +{
94 + return (strftime_l(s, maxsize, format, t, __get_locale()));
95 +}
96 +
78 97 static char *
79 -_fmt(const char *format, const struct tm *t, char *pt, const char * const ptlim)
98 +_fmt(const char *format, const struct tm *t, char *pt, const char * const ptlim,
99 + int *warnp, locale_t loc)
80 100 {
81 101 int Ealternative, Oalternative, PadIndex;
82 - struct lc_time_T *tptr = __get_current_time_locale();
102 + struct lc_time_T *tptr = __get_current_time_locale(loc);
83 103
84 104 #define PADDING(x) fmt_padding[x][PadIndex]
85 105
86 106 for (; *format; ++format) {
87 107 if (*format == '%') {
88 108 Ealternative = 0;
89 109 Oalternative = 0;
90 110 PadIndex = PAD_DEFAULT;
91 111 label:
92 112 switch (*++format) {
93 113 case '\0':
94 114 --format;
95 115 break;
96 116 case 'A':
97 117 pt = _add((t->tm_wday < 0 ||
98 118 t->tm_wday >= DAYSPERWEEK) ?
99 119 "?" : tptr->weekday[t->tm_wday],
100 120 pt, ptlim);
101 121 continue;
102 122 case 'a':
103 123 pt = _add((t->tm_wday < 0 ||
104 124 t->tm_wday >= DAYSPERWEEK) ?
105 125 "?" : tptr->wday[t->tm_wday],
106 126 pt, ptlim);
107 127 continue;
108 128 case 'B':
109 129 pt = _add((t->tm_mon < 0 ||
110 130 t->tm_mon >= MONSPERYEAR) ?
111 131 "?" : (tptr->month)[t->tm_mon],
112 132 pt, ptlim);
113 133 continue;
114 134 case 'b':
115 135 case 'h':
116 136 pt = _add((t->tm_mon < 0 ||
117 137 t->tm_mon >= MONSPERYEAR) ?
118 138 "?" : tptr->mon[t->tm_mon],
119 139 pt, ptlim);
120 140 continue;
121 141 case 'C':
122 142 /*
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
123 143 * %C used to do a...
124 144 * _fmt("%a %b %e %X %Y", t);
125 145 * ...whereas now POSIX 1003.2 calls for
126 146 * something completely different.
127 147 * (ado, 1993-05-24)
128 148 */
129 149 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
130 150 pt, ptlim);
131 151 continue;
132 152 case 'c':
133 - pt = _fmt(tptr->c_fmt, t, pt, ptlim);
153 + {
154 + int warn2 = IN_SOME;
155 +
156 + pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2,
157 + loc);
158 + /* XXX */
159 + }
134 160 continue;
135 161 case 'D':
136 - pt = _fmt("%m/%d/%y", t, pt, ptlim);
162 + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp,
163 + loc);
137 164 continue;
138 165 case 'd':
139 166 pt = _conv(t->tm_mday,
140 167 PADDING(PAD_FMT_DAYOFMONTH), pt, ptlim);
141 168 continue;
142 169 case 'E':
143 170 if (Ealternative || Oalternative)
144 171 break;
145 172 Ealternative++;
146 173 goto label;
147 174 case 'O':
148 175 /*
149 176 * C99 locale modifiers.
150 177 * The sequences
151 178 * %Ec %EC %Ex %EX %Ey %EY
152 179 * %Od %oe %OH %OI %Om %OM
153 180 * %OS %Ou %OU %OV %Ow %OW %Oy
154 181 * are supposed to provide alternate
155 182 * representations.
↓ open down ↓ |
9 lines elided |
↑ open up ↑ |
156 183 */
157 184 if (Ealternative || Oalternative)
158 185 break;
159 186 Oalternative++;
160 187 goto label;
161 188 case 'e':
162 189 pt = _conv(t->tm_mday,
163 190 PADDING(PAD_FMT_SDAYOFMONTH), pt, ptlim);
164 191 continue;
165 192 case 'F':
166 - pt = _fmt("%Y-%m-%d", t, pt, ptlim);
193 + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp,
194 + loc);
167 195 continue;
168 196 case 'H':
169 197 pt = _conv(t->tm_hour, PADDING(PAD_FMT_HMS),
170 198 pt, ptlim);
171 199 continue;
172 200 case 'I':
173 201 pt = _conv((t->tm_hour % 12) ?
174 202 (t->tm_hour % 12) : 12,
175 203 PADDING(PAD_FMT_HMS), pt, ptlim);
176 204 continue;
177 205 case 'j':
178 206 pt = _conv(t->tm_yday + 1,
179 207 PADDING(PAD_FMT_DAYOFYEAR), pt, ptlim);
180 208 continue;
181 209 case 'k':
182 210 /*
183 211 * This used to be...
184 212 * _conv(t->tm_hour % 12 ?
185 213 * t->tm_hour % 12 : 12, 2, ' ');
186 214 * ...and has been changed to the below to
187 215 * match SunOS 4.1.1 and Arnold Robbins'
188 216 * strftime version 3.0. That is, "%k" and
189 217 * "%l" have been swapped.
190 218 * (ado, 1993-05-24)
191 219 */
192 220 pt = _conv(t->tm_hour,
193 221 PADDING(PAD_FMT_SHMS), pt, ptlim);
194 222 continue;
195 223 case 'l':
196 224 /*
197 225 * This used to be...
198 226 * _conv(t->tm_hour, 2, ' ');
199 227 * ...and has been changed to the below to
200 228 * match SunOS 4.1.1 and Arnold Robbin's
201 229 * strftime version 3.0. That is, "%k" and
202 230 * "%l" have been swapped.
203 231 * (ado, 1993-05-24)
204 232 */
205 233 pt = _conv((t->tm_hour % 12) ?
206 234 (t->tm_hour % 12) : 12,
207 235 PADDING(PAD_FMT_SHMS), pt, ptlim);
208 236 continue;
209 237 case 'M':
210 238 pt = _conv(t->tm_min, PADDING(PAD_FMT_HMS),
211 239 pt, ptlim);
212 240 continue;
213 241 case 'm':
214 242 pt = _conv(t->tm_mon + 1,
215 243 PADDING(PAD_FMT_MONTH),
↓ open down ↓ |
39 lines elided |
↑ open up ↑ |
216 244 pt, ptlim);
217 245 continue;
218 246 case 'n':
219 247 pt = _add("\n", pt, ptlim);
220 248 continue;
221 249 case 'p':
222 250 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
223 251 tptr->pm : tptr->am, pt, ptlim);
224 252 continue;
225 253 case 'R':
226 - pt = _fmt("%H:%M", t, pt, ptlim);
254 + pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc);
227 255 continue;
228 256 case 'r':
229 - pt = _fmt(tptr->ampm_fmt, t, pt, ptlim);
257 + pt = _fmt(tptr->ampm_fmt, t, pt, ptlim,
258 + warnp, loc);
230 259 continue;
231 260 case 'S':
232 261 pt = _conv(t->tm_sec, PADDING(PAD_FMT_HMS),
233 262 pt, ptlim);
234 263 continue;
235 264
236 265 case 's':
237 266 {
238 267 struct tm tm;
239 268 char *buf;
240 -
269 +/* XXX */
241 270 tm = *t;
242 271 (void) asprintf(&buf, "%ld", mktime(&tm));
243 272 pt = _add(buf, pt, ptlim);
244 - continue;
245 273 }
246 -
274 + continue;
247 275 case 'T':
248 - pt = _fmt("%H:%M:%S", t, pt, ptlim);
276 + pt = _fmt("%H:%M:%S", t, pt, ptlim,
277 + warnp, loc);
249 278 continue;
250 279 case 't':
251 280 pt = _add("\t", pt, ptlim);
252 281 continue;
253 282 case 'U':
254 283 pt = _conv((t->tm_yday + DAYSPERWEEK -
255 284 t->tm_wday) / DAYSPERWEEK,
256 285 PADDING(PAD_FMT_WEEKOFYEAR),
257 286 pt, ptlim);
258 287 continue;
259 288 case 'u':
260 289 /*
261 290 * From Arnold Robbins' strftime version 3.0:
262 291 * "ISO 8601: Weekday as a decimal number
263 292 * [1 (Monday) - 7]"
264 293 * (ado, 1993-05-24)
265 294 */
266 295 pt = _conv((t->tm_wday == 0) ?
267 296 DAYSPERWEEK : t->tm_wday,
268 297 "%d", pt, ptlim);
269 298 continue;
270 299 case 'V': /* ISO 8601 week number */
271 300 case 'G': /* ISO 8601 year (four digits) */
272 301 case 'g': /* ISO 8601 year (two digits) */
273 302 /*
274 303 * From Arnold Robbins' strftime version 3.0: "the week number of the
275 304 * year (the first Monday as the first day of week 1) as a decimal number
276 305 * (01-53)."
277 306 * (ado, 1993-05-24)
278 307 *
279 308 * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
280 309 * "Week 01 of a year is per definition the first week which has the
281 310 * Thursday in this year, which is equivalent to the week which contains
282 311 * the fourth day of January. In other words, the first week of a new year
283 312 * is the week which has the majority of its days in the new year. Week 01
284 313 * might also contain days from the previous year and the week before week
285 314 * 01 of a year is the last week (52 or 53) of the previous year even if
286 315 * it contains days from the new year. A week starts with Monday (day 1)
287 316 * and ends with Sunday (day 7). For example, the first week of the year
288 317 * 1997 lasts from 1996-12-30 to 1997-01-05..."
289 318 * (ado, 1996-01-02)
290 319 */
291 320 {
292 321 int year;
293 322 int base;
294 323 int yday;
295 324 int wday;
296 325 int w;
297 326
298 327 year = t->tm_year;
299 328 base = TM_YEAR_BASE;
300 329 yday = t->tm_yday;
301 330 wday = t->tm_wday;
302 331 for (;;) {
303 332 int len;
304 333 int bot;
305 334 int top;
306 335
307 336 len = isleap_sum(year, base) ?
308 337 DAYSPERLYEAR : DAYSPERNYEAR;
309 338 /*
310 339 * What yday (-3 ... 3) does
311 340 * the ISO year begin on?
312 341 */
313 342 bot = ((yday + 11 - wday) %
314 343 DAYSPERWEEK) - 3;
315 344 /*
316 345 * What yday does the NEXT
317 346 * ISO year begin on?
318 347 */
319 348 top = bot - (len % DAYSPERWEEK);
320 349 if (top < -3)
321 350 top += DAYSPERWEEK;
322 351 top += len;
323 352 if (yday >= top) {
324 353 ++base;
325 354 w = 1;
326 355 break;
327 356 }
328 357 if (yday >= bot) {
329 358 w = 1 + ((yday - bot) /
330 359 DAYSPERWEEK);
331 360 break;
332 361 }
333 362 --base;
334 363 yday += isleap_sum(year, base) ?
335 364 DAYSPERLYEAR : DAYSPERNYEAR;
336 365 }
337 366 #ifdef XPG4_1994_04_09
338 367 if ((w == 52 && t->tm_mon == TM_JANUARY) ||
339 368 (w == 1 && t->tm_mon == TM_DECEMBER))
340 369 w = 53;
341 370 #endif /* defined XPG4_1994_04_09 */
342 371 if (*format == 'V')
343 372 pt = _conv(w,
344 373 PADDING(PAD_FMT_WEEKOFYEAR),
345 374 pt, ptlim);
346 375 else if (*format == 'g') {
347 376 pt = _yconv(year, base, 0, 1,
348 377 pt, ptlim);
349 378 } else
↓ open down ↓ |
91 lines elided |
↑ open up ↑ |
350 379 pt = _yconv(year, base, 1, 1,
351 380 pt, ptlim);
352 381 }
353 382 continue;
354 383 case 'v':
355 384 /*
356 385 * From Arnold Robbins' strftime version 3.0:
357 386 * "date as dd-bbb-YYYY"
358 387 * (ado, 1993-05-24)
359 388 */
360 - pt = _fmt("%e-%b-%Y", t, pt, ptlim);
389 + pt = _fmt("%e-%b-%Y", t, pt, ptlim,
390 + warnp, loc);
361 391 continue;
362 392 case 'W':
363 393 pt = _conv((t->tm_yday + DAYSPERWEEK -
364 394 (t->tm_wday ?
365 395 (t->tm_wday - 1) :
366 396 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
367 397 PADDING(PAD_FMT_WEEKOFYEAR),
368 398 pt, ptlim);
369 399 continue;
370 400 case 'w':
371 401 pt = _conv(t->tm_wday, "%d", pt, ptlim);
372 402 continue;
373 403 case 'X':
374 - pt = _fmt(tptr->X_fmt, t, pt, ptlim);
404 + pt = _fmt(tptr->X_fmt, t, pt, ptlim,
405 + warnp, loc);
375 406 continue;
376 407 case 'x':
377 - pt = _fmt(tptr->x_fmt, t, pt, ptlim);
408 + {
409 + int warn2 = IN_SOME;
410 +
411 + pt = _fmt(tptr->x_fmt, t, pt, ptlim,
412 + &warn2, loc);
413 + }
378 414 continue;
379 415 case 'y':
380 416 pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
381 417 pt, ptlim);
382 418 continue;
383 419 case 'Y':
384 420 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
385 421 pt, ptlim);
386 422 continue;
387 423 case 'Z':
388 424 if (t->tm_isdst >= 0)
389 425 pt = _add(tzname[t->tm_isdst != 0],
390 426 pt, ptlim);
391 427 /*
392 428 * C99 says that %Z must be replaced by the
393 429 * empty string if the time zone is not
394 430 * determinable.
395 431 */
396 432 continue;
397 433 case 'z':
398 434 {
399 435 int diff;
400 436 char const * sign;
401 437
402 438 if (t->tm_isdst < 0)
403 439 continue;
404 440 /*
405 441 * C99 says that the UTC offset must
406 442 * be computed by looking only at
407 443 * tm_isdst. This requirement is
408 444 * incorrect, since it means the code
409 445 * must rely on magic (in this case
410 446 * altzone and timezone), and the
411 447 * magic might not have the correct
412 448 * offset. Doing things correctly is
413 449 * tricky and requires disobeying C99;
414 450 * see GNU C strftime for details.
415 451 * For now, punt and conform to the
416 452 * standard, even though it's incorrect.
417 453 *
418 454 * C99 says that %z must be replaced by the
419 455 * empty string if the time zone is not
420 456 * determinable, so output nothing if the
421 457 * appropriate variables are not available.
422 458 */
423 459 if (t->tm_isdst == 0)
424 460 diff = -timezone;
425 461 else
426 462 diff = -altzone;
427 463 if (diff < 0) {
428 464 sign = "-";
429 465 diff = -diff;
430 466 } else
↓ open down ↓ |
43 lines elided |
↑ open up ↑ |
431 467 sign = "+";
432 468 pt = _add(sign, pt, ptlim);
433 469 diff /= SECSPERMIN;
434 470 diff = (diff / MINSPERHOUR) * 100 +
435 471 (diff % MINSPERHOUR);
436 472 pt = _conv(diff, PADDING(PAD_FMT_YEAR),
437 473 pt, ptlim);
438 474 }
439 475 continue;
440 476 case '+':
441 - pt = _fmt(tptr->date_fmt, t, pt, ptlim);
477 + pt = _fmt(tptr->date_fmt, t, pt, ptlim,
478 + warnp, loc);
442 479 continue;
443 480 case '-':
444 481 if (PadIndex != PAD_DEFAULT)
445 482 break;
446 483 PadIndex = PAD_LESS;
447 484 goto label;
448 485 case '_':
449 486 if (PadIndex != PAD_DEFAULT)
450 487 break;
451 488 PadIndex = PAD_SPACE;
452 489 goto label;
453 490 case '0':
454 491 if (PadIndex != PAD_DEFAULT)
455 492 break;
456 493 PadIndex = PAD_ZERO;
457 494 goto label;
458 495 case '%':
459 496 /*
460 497 * X311J/88-090 (4.12.3.5): if conversion char is
461 498 * undefined, behavior is undefined. Print out the
462 499 * character itself as printf(3) also does.
463 500 */
464 501 default:
465 502 break;
466 503 }
467 504 }
468 505 if (pt == ptlim)
469 506 break;
470 507 *pt++ = *format;
471 508 }
472 509 return (pt);
473 510 }
474 511
475 512 static char *
476 513 _conv(const int n, const char *format, char *const pt,
477 514 const char *const ptlim)
478 515 {
479 516 char buf[12];
480 517
481 518 (void) sprintf(buf, format, n);
482 519 return (_add(buf, pt, ptlim));
483 520 }
484 521
485 522 static char *
486 523 _add(const char *str, char *pt, const char *const ptlim)
487 524 {
488 525 while (pt < ptlim && (*pt = *str++) != '\0')
489 526 ++pt;
490 527 return (pt);
491 528 }
492 529
493 530 /*
494 531 * POSIX and the C Standard are unclear or inconsistent about
495 532 * what %C and %y do if the year is negative or exceeds 9999.
496 533 * Use the convention that %C concatenated with %y yields the
497 534 * same output as %Y, and that %Y contains at least 4 bytes,
498 535 * with more only if necessary.
499 536 */
500 537
501 538 static char *
502 539 _yconv(const int a, const int b, const int convert_top, const int convert_yy,
503 540 char *pt, const char * const ptlim)
504 541 {
505 542 register int lead;
506 543 register int trail;
507 544
508 545 #define DIVISOR 100
509 546 trail = a % DIVISOR + b % DIVISOR;
510 547 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
511 548 trail %= DIVISOR;
512 549 if (trail < 0 && lead > 0) {
513 550 trail += DIVISOR;
514 551 --lead;
515 552 } else if (lead < 0 && trail > 0) {
516 553 trail -= DIVISOR;
517 554 ++lead;
518 555 }
519 556 if (convert_top) {
520 557 if (lead == 0 && trail < 0)
521 558 pt = _add("-0", pt, ptlim);
522 559 else pt = _conv(lead, "%02d", pt, ptlim);
523 560 }
524 561 if (convert_yy)
525 562 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
526 563 return (pt);
527 564 }
↓ open down ↓ |
76 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX