strftime.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. /* $OpenBSD: strftime.c,v 1.21 2012/09/13 11:14:20 millert Exp $ */
  2. #include "private.h"
  3. /*
  4. ** Copyright (c) 1989, 1993
  5. ** The Regents of the University of California. All rights reserved.
  6. **
  7. ** Redistribution and use in source and binary forms, with or without
  8. ** modification, are permitted provided that the following conditions
  9. ** are met:
  10. ** 1. Redistributions of source code must retain the above copyright
  11. ** notice, this list of conditions and the following disclaimer.
  12. ** 2. Redistributions in binary form must reproduce the above copyright
  13. ** notice, this list of conditions and the following disclaimer in the
  14. ** documentation and/or other materials provided with the distribution.
  15. ** 3. Neither the name of the University nor the names of its contributors
  16. ** may be used to endorse or promote products derived from this software
  17. ** without specific prior written permission.
  18. **
  19. ** THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  20. ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. ** ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  23. ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  25. ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26. ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27. ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  28. ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. ** SUCH DAMAGE.
  30. */
  31. #include "tzfile.h"
  32. //#include "fcntl.h"
  33. //#include "locale.h"
  34. struct lc_time_T {
  35. const char * mon[MONSPERYEAR];
  36. const char * month[MONSPERYEAR];
  37. const char * wday[DAYSPERWEEK];
  38. const char * weekday[DAYSPERWEEK];
  39. const char * X_fmt;
  40. const char * x_fmt;
  41. const char * c_fmt;
  42. const char * am;
  43. const char * pm;
  44. const char * date_fmt;
  45. };
  46. #ifdef LOCALE_HOME
  47. #include "sys/stat.h"
  48. static struct lc_time_T localebuf;
  49. static struct lc_time_T * _loc(void);
  50. #define Locale _loc()
  51. #endif /* defined LOCALE_HOME */
  52. #ifndef LOCALE_HOME
  53. #define Locale (&C_time_locale)
  54. #endif /* !defined LOCALE_HOME */
  55. static const struct lc_time_T C_time_locale = {
  56. {
  57. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  58. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  59. }, {
  60. "January", "February", "March", "April", "May", "June",
  61. "July", "August", "September", "October", "November", "December"
  62. }, {
  63. "Sun", "Mon", "Tue", "Wed",
  64. "Thu", "Fri", "Sat"
  65. }, {
  66. "Sunday", "Monday", "Tuesday", "Wednesday",
  67. "Thursday", "Friday", "Saturday"
  68. },
  69. /* X_fmt */
  70. "%H:%M:%S",
  71. /*
  72. ** x_fmt
  73. ** C99 requires this format.
  74. ** Using just numbers (as here) makes Quakers happier;
  75. ** it's also compatible with SVR4.
  76. */
  77. "%m/%d/%y",
  78. /*
  79. ** c_fmt
  80. ** C99 requires this format.
  81. ** Previously this code used "%D %X", but we now conform to C99.
  82. ** Note that
  83. ** "%a %b %d %H:%M:%S %Y"
  84. ** is used by Solaris 2.3.
  85. */
  86. "%a %b %e %T %Y",
  87. /* am */
  88. "AM",
  89. /* pm */
  90. "PM",
  91. /* date_fmt */
  92. "%a %b %e %H:%M:%S %Z %Y"
  93. };
  94. static char * _add(const char *, char *, const char *);
  95. static char * _conv(int, const char *, char *, const char *);
  96. static char * _fmt(const char *, const struct tm *, char *, const char *,
  97. int *);
  98. static char * _yconv(int, int, int, int, char *, const char *);
  99. //extern char * tzname[];
  100. #ifndef YEAR_2000_NAME
  101. #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
  102. #endif /* !defined YEAR_2000_NAME */
  103. #define IN_NONE 0
  104. #define IN_SOME 1
  105. #define IN_THIS 2
  106. #define IN_ALL 3
  107. size_t
  108. strftime(s, maxsize, format, t)
  109. char * const s;
  110. const size_t maxsize;
  111. const char * const format;
  112. const struct tm * const t;
  113. {
  114. char * p;
  115. int warn;
  116. //tzset();
  117. #ifdef LOCALE_HOME
  118. localebuf.mon[0] = 0;
  119. #endif /* defined LOCALE_HOME */
  120. warn = IN_NONE;
  121. p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
  122. if (p == s + maxsize) {
  123. if (maxsize > 0)
  124. s[maxsize - 1] = '\0';
  125. return 0;
  126. }
  127. *p = '\0';
  128. return p - s;
  129. }
  130. static char *
  131. _fmt(format, t, pt, ptlim, warnp)
  132. const char * format;
  133. const struct tm * const t;
  134. char * pt;
  135. const char * const ptlim;
  136. int * warnp;
  137. {
  138. for ( ; *format; ++format) {
  139. if (*format == '%') {
  140. label:
  141. switch (*++format) {
  142. case '\0':
  143. --format;
  144. break;
  145. case 'A':
  146. pt = _add((t->tm_wday < 0 ||
  147. t->tm_wday >= DAYSPERWEEK) ?
  148. "?" : Locale->weekday[t->tm_wday],
  149. pt, ptlim);
  150. continue;
  151. case 'a':
  152. pt = _add((t->tm_wday < 0 ||
  153. t->tm_wday >= DAYSPERWEEK) ?
  154. "?" : Locale->wday[t->tm_wday],
  155. pt, ptlim);
  156. continue;
  157. case 'B':
  158. pt = _add((t->tm_mon < 0 ||
  159. t->tm_mon >= MONSPERYEAR) ?
  160. "?" : Locale->month[t->tm_mon],
  161. pt, ptlim);
  162. continue;
  163. case 'b':
  164. case 'h':
  165. pt = _add((t->tm_mon < 0 ||
  166. t->tm_mon >= MONSPERYEAR) ?
  167. "?" : Locale->mon[t->tm_mon],
  168. pt, ptlim);
  169. continue;
  170. case 'C':
  171. /*
  172. ** %C used to do a...
  173. ** _fmt("%a %b %e %X %Y", t);
  174. ** ...whereas now POSIX 1003.2 calls for
  175. ** something completely different.
  176. ** (ado, 1993-05-24)
  177. */
  178. pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
  179. pt, ptlim);
  180. continue;
  181. case 'c':
  182. {
  183. int warn2 = IN_SOME;
  184. pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
  185. if (warn2 == IN_ALL)
  186. warn2 = IN_THIS;
  187. if (warn2 > *warnp)
  188. *warnp = warn2;
  189. }
  190. continue;
  191. case 'D':
  192. pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
  193. continue;
  194. case 'd':
  195. pt = _conv(t->tm_mday, "%02d", pt, ptlim);
  196. continue;
  197. case 'E':
  198. case 'O':
  199. /*
  200. ** C99 locale modifiers.
  201. ** The sequences
  202. ** %Ec %EC %Ex %EX %Ey %EY
  203. ** %Od %oe %OH %OI %Om %OM
  204. ** %OS %Ou %OU %OV %Ow %OW %Oy
  205. ** are supposed to provide alternate
  206. ** representations.
  207. */
  208. goto label;
  209. case 'e':
  210. pt = _conv(t->tm_mday, "%2d", pt, ptlim);
  211. continue;
  212. case 'F':
  213. pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
  214. continue;
  215. case 'H':
  216. pt = _conv(t->tm_hour, "%02d", pt, ptlim);
  217. continue;
  218. case 'I':
  219. pt = _conv((t->tm_hour % 12) ?
  220. (t->tm_hour % 12) : 12,
  221. "%02d", pt, ptlim);
  222. continue;
  223. case 'j':
  224. pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
  225. continue;
  226. case 'k':
  227. /*
  228. ** This used to be...
  229. ** _conv(t->tm_hour % 12 ?
  230. ** t->tm_hour % 12 : 12, 2, ' ');
  231. ** ...and has been changed to the below to
  232. ** match SunOS 4.1.1 and Arnold Robbins'
  233. ** strftime version 3.0. That is, "%k" and
  234. ** "%l" have been swapped.
  235. ** (ado, 1993-05-24)
  236. */
  237. pt = _conv(t->tm_hour, "%2d", pt, ptlim);
  238. continue;
  239. #ifdef KITCHEN_SINK
  240. case 'K':
  241. /*
  242. ** After all this time, still unclaimed!
  243. */
  244. pt = _add("kitchen sink", pt, ptlim);
  245. continue;
  246. #endif /* defined KITCHEN_SINK */
  247. case 'l':
  248. /*
  249. ** This used to be...
  250. ** _conv(t->tm_hour, 2, ' ');
  251. ** ...and has been changed to the below to
  252. ** match SunOS 4.1.1 and Arnold Robbin's
  253. ** strftime version 3.0. That is, "%k" and
  254. ** "%l" have been swapped.
  255. ** (ado, 1993-05-24)
  256. */
  257. pt = _conv((t->tm_hour % 12) ?
  258. (t->tm_hour % 12) : 12,
  259. "%2d", pt, ptlim);
  260. continue;
  261. case 'M':
  262. pt = _conv(t->tm_min, "%02d", pt, ptlim);
  263. continue;
  264. case 'm':
  265. pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
  266. continue;
  267. case 'n':
  268. pt = _add("\n", pt, ptlim);
  269. continue;
  270. case 'p':
  271. pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
  272. Locale->pm :
  273. Locale->am,
  274. pt, ptlim);
  275. continue;
  276. case 'R':
  277. pt = _fmt("%H:%M", t, pt, ptlim, warnp);
  278. continue;
  279. case 'r':
  280. pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
  281. continue;
  282. case 'S':
  283. pt = _conv(t->tm_sec, "%02d", pt, ptlim);
  284. continue;
  285. case 's':
  286. #if 0
  287. {
  288. struct tm tm;
  289. char buf[INT_STRLEN_MAXIMUM(
  290. time_t) + 1];
  291. time_t mkt;
  292. tm = *t;
  293. mkt = mktime(&tm);
  294. if (TYPE_SIGNED(time_t))
  295. (void) snprintf(buf, sizeof buf,
  296. "%ld", (long) mkt);
  297. else (void) snprintf(buf, sizeof buf,
  298. "%lu", (unsigned long) mkt);
  299. pt = _add(buf, pt, ptlim);
  300. }
  301. #endif
  302. continue;
  303. case 'T':
  304. pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
  305. continue;
  306. case 't':
  307. pt = _add("\t", pt, ptlim);
  308. continue;
  309. case 'U':
  310. pt = _conv((t->tm_yday + DAYSPERWEEK -
  311. t->tm_wday) / DAYSPERWEEK,
  312. "%02d", pt, ptlim);
  313. continue;
  314. case 'u':
  315. /*
  316. ** From Arnold Robbins' strftime version 3.0:
  317. ** "ISO 8601: Weekday as a decimal number
  318. ** [1 (Monday) - 7]"
  319. ** (ado, 1993-05-24)
  320. */
  321. pt = _conv((t->tm_wday == 0) ?
  322. DAYSPERWEEK : t->tm_wday,
  323. "%d", pt, ptlim);
  324. continue;
  325. case 'V': /* ISO 8601 week number */
  326. case 'G': /* ISO 8601 year (four digits) */
  327. case 'g': /* ISO 8601 year (two digits) */
  328. /*
  329. ** From Arnold Robbins' strftime version 3.0: "the week number of the
  330. ** year (the first Monday as the first day of week 1) as a decimal number
  331. ** (01-53)."
  332. ** (ado, 1993-05-24)
  333. **
  334. ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
  335. ** "Week 01 of a year is per definition the first week which has the
  336. ** Thursday in this year, which is equivalent to the week which contains
  337. ** the fourth day of January. In other words, the first week of a new year
  338. ** is the week which has the majority of its days in the new year. Week 01
  339. ** might also contain days from the previous year and the week before week
  340. ** 01 of a year is the last week (52 or 53) of the previous year even if
  341. ** it contains days from the new year. A week starts with Monday (day 1)
  342. ** and ends with Sunday (day 7). For example, the first week of the year
  343. ** 1997 lasts from 1996-12-30 to 1997-01-05..."
  344. ** (ado, 1996-01-02)
  345. */
  346. {
  347. int year;
  348. int base;
  349. int yday;
  350. int wday;
  351. int w;
  352. year = t->tm_year;
  353. base = TM_YEAR_BASE;
  354. yday = t->tm_yday;
  355. wday = t->tm_wday;
  356. for ( ; ; ) {
  357. int len;
  358. int bot;
  359. int top;
  360. len = isleap_sum(year, base) ?
  361. DAYSPERLYEAR :
  362. DAYSPERNYEAR;
  363. /*
  364. ** What yday (-3 ... 3) does
  365. ** the ISO year begin on?
  366. */
  367. bot = ((yday + 11 - wday) %
  368. DAYSPERWEEK) - 3;
  369. /*
  370. ** What yday does the NEXT
  371. ** ISO year begin on?
  372. */
  373. top = bot -
  374. (len % DAYSPERWEEK);
  375. if (top < -3)
  376. top += DAYSPERWEEK;
  377. top += len;
  378. if (yday >= top) {
  379. ++base;
  380. w = 1;
  381. break;
  382. }
  383. if (yday >= bot) {
  384. w = 1 + ((yday - bot) /
  385. DAYSPERWEEK);
  386. break;
  387. }
  388. --base;
  389. yday += isleap_sum(year, base) ?
  390. DAYSPERLYEAR :
  391. DAYSPERNYEAR;
  392. }
  393. #ifdef XPG4_1994_04_09
  394. if ((w == 52 &&
  395. t->tm_mon == TM_JANUARY) ||
  396. (w == 1 &&
  397. t->tm_mon == TM_DECEMBER))
  398. w = 53;
  399. #endif /* defined XPG4_1994_04_09 */
  400. if (*format == 'V')
  401. pt = _conv(w, "%02d",
  402. pt, ptlim);
  403. else if (*format == 'g') {
  404. *warnp = IN_ALL;
  405. pt = _yconv(year, base, 0, 1,
  406. pt, ptlim);
  407. } else pt = _yconv(year, base, 1, 1,
  408. pt, ptlim);
  409. }
  410. continue;
  411. case 'v':
  412. /*
  413. ** From Arnold Robbins' strftime version 3.0:
  414. ** "date as dd-bbb-YYYY"
  415. ** (ado, 1993-05-24)
  416. */
  417. pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
  418. continue;
  419. case 'W':
  420. pt = _conv((t->tm_yday + DAYSPERWEEK -
  421. (t->tm_wday ?
  422. (t->tm_wday - 1) :
  423. (DAYSPERWEEK - 1))) / DAYSPERWEEK,
  424. "%02d", pt, ptlim);
  425. continue;
  426. case 'w':
  427. pt = _conv(t->tm_wday, "%d", pt, ptlim);
  428. continue;
  429. case 'X':
  430. pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
  431. continue;
  432. case 'x':
  433. {
  434. int warn2 = IN_SOME;
  435. pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
  436. if (warn2 == IN_ALL)
  437. warn2 = IN_THIS;
  438. if (warn2 > *warnp)
  439. *warnp = warn2;
  440. }
  441. continue;
  442. case 'y':
  443. *warnp = IN_ALL;
  444. pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
  445. pt, ptlim);
  446. continue;
  447. case 'Y':
  448. pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
  449. pt, ptlim);
  450. continue;
  451. case 'Z':
  452. #if 0
  453. #ifdef TM_ZONE
  454. if (t->TM_ZONE != NULL)
  455. pt = _add(t->TM_ZONE, pt, ptlim);
  456. else
  457. #endif /* defined TM_ZONE */
  458. if (t->tm_isdst >= 0)
  459. pt = _add(tzname[t->tm_isdst != 0],
  460. pt, ptlim);
  461. #endif
  462. /*
  463. ** C99 says that %Z must be replaced by the
  464. ** empty string if the time zone is not
  465. ** determinable.
  466. */
  467. continue;
  468. case 'z':
  469. #if 0
  470. {
  471. int diff;
  472. char const * sign;
  473. if (t->tm_isdst < 0)
  474. continue;
  475. #ifdef TM_GMTOFF
  476. diff = t->TM_GMTOFF;
  477. #else /* !defined TM_GMTOFF */
  478. /*
  479. ** C99 says that the UTC offset must
  480. ** be computed by looking only at
  481. ** tm_isdst. This requirement is
  482. ** incorrect, since it means the code
  483. ** must rely on magic (in this case
  484. ** altzone and timezone), and the
  485. ** magic might not have the correct
  486. ** offset. Doing things correctly is
  487. ** tricky and requires disobeying C99;
  488. ** see GNU C strftime for details.
  489. ** For now, punt and conform to the
  490. ** standard, even though it's incorrect.
  491. **
  492. ** C99 says that %z must be replaced by the
  493. ** empty string if the time zone is not
  494. ** determinable, so output nothing if the
  495. ** appropriate variables are not available.
  496. */
  497. if (t->tm_isdst == 0)
  498. #ifdef USG_COMPAT
  499. diff = -timezone;
  500. #else /* !defined USG_COMPAT */
  501. continue;
  502. #endif /* !defined USG_COMPAT */
  503. else
  504. #ifdef ALTZONE
  505. diff = -altzone;
  506. #else /* !defined ALTZONE */
  507. continue;
  508. #endif /* !defined ALTZONE */
  509. #endif /* !defined TM_GMTOFF */
  510. if (diff < 0) {
  511. sign = "-";
  512. diff = -diff;
  513. } else sign = "+";
  514. pt = _add(sign, pt, ptlim);
  515. diff /= SECSPERMIN;
  516. diff = (diff / MINSPERHOUR) * 100 +
  517. (diff % MINSPERHOUR);
  518. pt = _conv(diff, "%04d", pt, ptlim);
  519. }
  520. #endif
  521. continue;
  522. case '+':
  523. pt = _fmt(Locale->date_fmt, t, pt, ptlim,
  524. warnp);
  525. continue;
  526. case '%':
  527. /*
  528. ** X311J/88-090 (4.12.3.5): if conversion char is
  529. ** undefined, behavior is undefined. Print out the
  530. ** character itself as printf(3) also does.
  531. */
  532. default:
  533. break;
  534. }
  535. }
  536. if (pt == ptlim)
  537. break;
  538. *pt++ = *format;
  539. }
  540. return pt;
  541. }
  542. static char *
  543. _conv(n, format, pt, ptlim)
  544. const int n;
  545. const char * const format;
  546. char * const pt;
  547. const char * const ptlim;
  548. {
  549. char buf[INT_STRLEN_MAXIMUM(int) + 1];
  550. (void) snprintf(buf, sizeof buf, format, n);
  551. return _add(buf, pt, ptlim);
  552. }
  553. static char *
  554. _add(str, pt, ptlim)
  555. const char * str;
  556. char * pt;
  557. const char * const ptlim;
  558. {
  559. while (pt < ptlim && (*pt = *str++) != '\0')
  560. ++pt;
  561. return pt;
  562. }
  563. /*
  564. ** POSIX and the C Standard are unclear or inconsistent about
  565. ** what %C and %y do if the year is negative or exceeds 9999.
  566. ** Use the convention that %C concatenated with %y yields the
  567. ** same output as %Y, and that %Y contains at least 4 bytes,
  568. ** with more only if necessary.
  569. */
  570. static char *
  571. _yconv(a, b, convert_top, convert_yy, pt, ptlim)
  572. const int a;
  573. const int b;
  574. const int convert_top;
  575. const int convert_yy;
  576. char * pt;
  577. const char * const ptlim;
  578. {
  579. register int lead;
  580. register int trail;
  581. #define DIVISOR 100
  582. trail = a % DIVISOR + b % DIVISOR;
  583. lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
  584. trail %= DIVISOR;
  585. if (trail < 0 && lead > 0) {
  586. trail += DIVISOR;
  587. --lead;
  588. } else if (lead < 0 && trail > 0) {
  589. trail -= DIVISOR;
  590. ++lead;
  591. }
  592. if (convert_top) {
  593. if (lead == 0 && trail < 0)
  594. pt = _add("-0", pt, ptlim);
  595. else pt = _conv(lead, "%02d", pt, ptlim);
  596. }
  597. if (convert_yy)
  598. pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
  599. return pt;
  600. }
  601. #ifdef LOCALE_HOME
  602. static struct lc_time_T *
  603. _loc(void)
  604. {
  605. static const char locale_home[] = LOCALE_HOME;
  606. static const char lc_time[] = "LC_TIME";
  607. static char * locale_buf;
  608. int fd;
  609. int oldsun; /* "...ain't got nothin' to do..." */
  610. int len;
  611. char * lbuf;
  612. char * nlbuf;
  613. char * name;
  614. char * p;
  615. const char ** ap;
  616. const char * plim;
  617. char filename[FILENAME_MAX];
  618. struct stat st;
  619. size_t namesize;
  620. size_t bufsize;
  621. /*
  622. ** Use localebuf.mon[0] to signal whether locale is already set up.
  623. */
  624. if (localebuf.mon[0])
  625. return &localebuf;
  626. name = setlocale(LC_TIME, (char *) NULL);
  627. if (name == NULL || *name == '\0')
  628. goto no_locale;
  629. /*
  630. ** If the locale name is the same as our cache, use the cache.
  631. */
  632. lbuf = locale_buf;
  633. if (lbuf != NULL && strcmp(name, lbuf) == 0) {
  634. p = lbuf;
  635. for (ap = (const char **) &localebuf;
  636. ap < (const char **) (&localebuf + 1);
  637. ++ap)
  638. *ap = p += strlen(p) + 1;
  639. return &localebuf;
  640. }
  641. /*
  642. ** Slurp the locale file into the cache.
  643. */
  644. namesize = strlen(name) + 1;
  645. if (sizeof filename <
  646. ((sizeof locale_home) + namesize + (sizeof lc_time)))
  647. goto no_locale;
  648. oldsun = 0;
  649. len = snprintf(filename, sizeof filename, "%s/%s/%s", locale_home,
  650. name, lc_time);
  651. if (len < 0 || len >= sizeof filename)
  652. goto no_locale;
  653. fd = open(filename, O_RDONLY);
  654. if (fd < 0) {
  655. /*
  656. ** Old Sun systems have a different naming and data convention.
  657. */
  658. oldsun = 1;
  659. len = snprintf(filename, sizeof filename, "%s/%s/%s",
  660. locale_home, lc_time, name);
  661. if (len < 0 || len >= sizeof filename)
  662. goto no_locale;
  663. fd = open(filename, O_RDONLY);
  664. if (fd < 0)
  665. goto no_locale;
  666. }
  667. if (fstat(fd, &st) != 0)
  668. goto bad_locale;
  669. if (st.st_size <= 0)
  670. goto bad_locale;
  671. bufsize = namesize + st.st_size;
  672. locale_buf = NULL;
  673. nlbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
  674. if (nlbuf == NULL) {
  675. if (lbuf)
  676. free(lbuf);
  677. lbuf = NULL;
  678. goto bad_locale;
  679. }
  680. lbuf = nlbuf;
  681. (void) strlcpy(lbuf, name, bufsize);
  682. p = lbuf + namesize;
  683. plim = p + st.st_size;
  684. if (read(fd, p, (size_t) st.st_size) != st.st_size)
  685. goto bad_lbuf;
  686. if (close(fd) != 0)
  687. goto bad_lbuf;
  688. /*
  689. ** Parse the locale file into localebuf.
  690. */
  691. if (plim[-1] != '\n')
  692. goto bad_lbuf;
  693. for (ap = (const char **) &localebuf;
  694. ap < (const char **) (&localebuf + 1);
  695. ++ap) {
  696. if (p == plim)
  697. goto bad_lbuf;
  698. *ap = p;
  699. while (*p != '\n')
  700. ++p;
  701. *p++ = '\0';
  702. }
  703. if (oldsun) {
  704. /*
  705. ** SunOS 4 used an obsolescent format; see localdtconv(3).
  706. ** c_fmt had the ``short format for dates and times together''
  707. ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
  708. ** date_fmt had the ``long format for dates''
  709. ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
  710. ** Discard the latter in favor of the former.
  711. */
  712. localebuf.date_fmt = localebuf.c_fmt;
  713. }
  714. /*
  715. ** Record the successful parse in the cache.
  716. */
  717. locale_buf = lbuf;
  718. return &localebuf;
  719. bad_lbuf:
  720. free(lbuf);
  721. bad_locale:
  722. (void) close(fd);
  723. no_locale:
  724. localebuf = C_time_locale;
  725. locale_buf = NULL;
  726. return &localebuf;
  727. }
  728. #endif /* defined LOCALE_HOME */