tzstrftime.c 22 KB

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