strftime.c 18 KB

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