1 /*
2  * Copyright (c) 1999
3  * Silicon Graphics Computer Systems, Inc.
4  *
5  * Copyright (c) 1999
6  * Boris Fomitchev
7  *
8  * This material is provided "as is", with absolutely no warranty expressed
9  * or implied. Any use is at your own risk.
10  *
11  * Permission to use or copy this software for any purpose is hereby granted
12  * without fee, provided the above notices are retained on all copies.
13  * Permission to modify the code and to distribute modified code is granted,
14  * provided the above notices are retained, and a notice that the code was
15  * modified is included with the above copyright notice.
16  *
17  */
18 
19 #include "stlport_prefix.h"
20 
21 #include <cstdio>
22 #include <locale>
23 #include <istream>
24 
25 #include "c_locale.h"
26 #include "acquire_release.h"
27 
28 _STLP_BEGIN_NAMESPACE
29 
30 _STLP_MOVE_TO_PRIV_NAMESPACE
31 
32 // default "C" values for month and day names
33 
34 const char default_dayname[][14] = {
35   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
36   "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
37   "Friday", "Saturday"};
38 
39 const char default_monthname[][24] = {
40   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
41   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
42   "January", "February", "March", "April", "May", "June",
43   "July", "August", "September", "October", "November", "December"};
44 
45 #ifndef _STLP_NO_WCHAR_T
46 const wchar_t default_wdayname[][14] = {
47   L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat",
48   L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday",
49   L"Friday", L"Saturday"};
50 
51 const wchar_t default_wmonthname[][24] = {
52   L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun",
53   L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec",
54   L"January", L"February", L"March", L"April", L"May", L"June",
55   L"July", L"August", L"September", L"October", L"November", L"December"};
56 #endif
57 
58 #if defined (__BORLANDC__)
59 _Time_Info time_init<char>::_M_timeinfo;
60 #  ifndef _STLP_NO_WCHAR_T
61 _WTime_Info time_init<wchar_t>::_M_timeinfo;
62 #  endif
63 #endif
64 
65 // _Init_time_info: initialize table with
66 // "C" values (note these are not defined in the C standard, so this
67 // is somewhat arbitrary).
68 
69 static void _Init_timeinfo_base(_Time_Info_Base& table) {
70   table._M_time_format = "%H:%M:%S";
71   table._M_date_format = "%m/%d/%y";
72   table._M_date_time_format = "%m/%d/%y";
73 }
74 
75 static void _Init_timeinfo(_Time_Info& table) {
76   int i;
77   for (i = 0; i < 14; ++i)
78     table._M_dayname[i] = default_dayname[i];
79   for (i = 0; i < 24; ++i)
80     table._M_monthname[i] = default_monthname[i];
81   table._M_am_pm[0] = "AM";
82   table._M_am_pm[1] = "PM";
83   _Init_timeinfo_base(table);
84 }
85 
86 #ifndef _STLP_NO_WCHAR_T
87 static void _Init_timeinfo(_WTime_Info& table) {
88   int i;
89   for (i = 0; i < 14; ++i)
90     table._M_dayname[i] = default_wdayname[i];
91   for (i = 0; i < 24; ++i)
92     table._M_monthname[i] = default_wmonthname[i];
93   table._M_am_pm[0] = L"AM";
94   table._M_am_pm[1] = L"PM";
95   _Init_timeinfo_base(table);
96 }
97 #endif
98 
99 static void _Init_timeinfo_base(_Time_Info_Base& table, _Locale_time * time) {
100   table._M_time_format = _Locale_t_fmt(time);
101   if ( table._M_time_format == "%T" ) {
102     table._M_time_format = "%H:%M:%S";
103   } else if ( table._M_time_format == "%r" ) {
104     table._M_time_format = "%I:%M:%S %p";
105   } else if ( table._M_time_format == "%R" ) {
106     table._M_time_format = "%H:%M";
107   }
108   table._M_date_format = _Locale_d_fmt(time);
109   table._M_date_time_format = _Locale_d_t_fmt(time);
110   table._M_long_date_format = _Locale_long_d_fmt(time);
111   table._M_long_date_time_format = _Locale_long_d_t_fmt(time);
112 }
113 
114 static void _Init_timeinfo(_Time_Info& table, _Locale_time * time) {
115   int i;
116   for (i = 0; i < 7; ++i)
117     table._M_dayname[i] = _Locale_abbrev_dayofweek(time, i);
118   for (i = 0; i < 7; ++i)
119     table._M_dayname[i+7] = _Locale_full_dayofweek(time, i);
120   for (i = 0; i < 12; ++i)
121     table._M_monthname[i] = _Locale_abbrev_monthname(time, i);
122   for (i = 0; i < 12; ++i)
123     table._M_monthname[i+12] = _Locale_full_monthname(time, i);
124   table._M_am_pm[0] = _Locale_am_str(time);
125   table._M_am_pm[1] = _Locale_pm_str(time);
126   _Init_timeinfo_base(table, time);
127 }
128 
129 #ifndef _STLP_NO_WCHAR_T
130 static void _Init_timeinfo(_WTime_Info& table, _Locale_time * time) {
131   wchar_t buf[128];
132   int i;
133   for (i = 0; i < 7; ++i)
134     table._M_dayname[i] = _WLocale_abbrev_dayofweek(time, i, _STLP_ARRAY_AND_SIZE(buf));
135   for (i = 0; i < 7; ++i)
136     table._M_dayname[i+7] = _WLocale_full_dayofweek(time, i, _STLP_ARRAY_AND_SIZE(buf));
137   for (i = 0; i < 12; ++i)
138     table._M_monthname[i] = _WLocale_abbrev_monthname(time, i, _STLP_ARRAY_AND_SIZE(buf));
139   for (i = 0; i < 12; ++i)
140     table._M_monthname[i+12] = _WLocale_full_monthname(time, i, _STLP_ARRAY_AND_SIZE(buf));
141   table._M_am_pm[0] = _WLocale_am_str(time, _STLP_ARRAY_AND_SIZE(buf));
142   table._M_am_pm[1] = _WLocale_pm_str(time, _STLP_ARRAY_AND_SIZE(buf));
143   _Init_timeinfo_base(table, time);
144 }
145 #endif
146 
147 template <class _Ch, class _TimeInfo>
148 void __subformat(_STLP_BASIC_IOSTRING(_Ch) &buf, const ctype<_Ch>& ct,
149                  const string& format, const _TimeInfo& table, const tm* t) {
150   const char * cp = format.data();
151   const char * cp_end = cp + format.size();
152   while (cp != cp_end) {
153     if (*cp == '%') {
154       char mod = 0;
155       ++cp;
156       if (*cp == '#') {
157         mod = *cp; ++cp;
158       }
159       __write_formatted_timeT(buf, ct, *cp++, mod, table, t);
160     } else
161       buf.append(1, *cp++);
162   }
163 }
164 
165 static void __append(__iostring &buf, const string& name)
166 { buf.append(name.data(), name.data() + name.size()); }
167 
168 static void __append(__iowstring &buf, const wstring& name)
169 { buf.append(name.data(), name.data() + name.size()); }
170 
171 static void __append(__iostring &buf, char *first, char *last, const ctype<char>& /* ct */)
172 { buf.append(first, last); }
173 
174 static void __append(__iowstring &buf, char *first, char *last, const ctype<wchar_t>& ct) {
175   wchar_t _wbuf[64];
176   ct.widen(first, last, _wbuf);
177   buf.append(_wbuf, _wbuf + (last - first));
178 }
179 
180 #if defined (__GNUC__)
181 /* The number of days from the first day of the first ISO week of this
182    year to the year day YDAY with week day WDAY.  ISO weeks start on
183    Monday; the first ISO week has the year's first Thursday.  YDAY may
184    be as small as YDAY_MINIMUM.  */
185 #  define __ISO_WEEK_START_WDAY 1 /* Monday */
186 #  define __ISO_WEEK1_WDAY 4 /* Thursday */
187 #  define __YDAY_MINIMUM (-366)
188 #  define __TM_YEAR_BASE 1900
189 static int
190 __iso_week_days(int yday, int wday) {
191   /* Add enough to the first operand of % to make it nonnegative.  */
192   int big_enough_multiple_of_7 = (-__YDAY_MINIMUM / 7 + 2) * 7;
193   return (yday
194           - (yday - wday + __ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
195           + __ISO_WEEK1_WDAY - __ISO_WEEK_START_WDAY);
196 }
197 
198 #  define __is_leap(year)\
199   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
200 
201 #endif
202 
203 #define __hour12(hour) \
204   (((hour) % 12 == 0) ? (12) : (hour) % 12)
205 
206 #if !defined (_STLP_USE_SAFE_STRING_FUNCTIONS)
207 #  define _STLP_SPRINTF sprintf
208 #else
209 #  define _STLP_SPRINTF sprintf_s
210 #endif
211 
212 template <class _Ch, class _TimeInfo>
213 void _STLP_CALL __write_formatted_timeT(_STLP_BASIC_IOSTRING(_Ch) &buf,
214                                         const ctype<_Ch>& ct,
215                                         char format, char modifier,
216                                         const _TimeInfo& table, const tm* t) {
217   char _buf[64];
218   char *_bend;
219 
220   switch (format) {
221     case 'a':
222       __append(buf, table._M_dayname[t->tm_wday]);
223       break;
224 
225     case 'A':
226       __append(buf, table._M_dayname[t->tm_wday + 7]);
227       break;
228 
229     case 'b':
230       __append(buf, table._M_monthname[t->tm_mon]);
231       break;
232 
233     case 'B':
234       __append(buf, table._M_monthname[t->tm_mon + 12]);
235       break;
236 
237     case 'c':
238       __subformat(buf, ct, (modifier != '#') ? table._M_date_time_format
239                                              : table._M_long_date_time_format, table, t);
240       break;
241 
242     case 'd':
243       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_mday);
244       __append(buf, _buf, ((long)t->tm_mday < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
245       break;
246 
247     case 'e':
248       _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_mday);
249       __append(buf, _buf, _buf + 2, ct);
250       break;
251 
252     case 'H':
253       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_hour);
254       __append(buf, _buf, ((long)t->tm_hour < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
255       break;
256 
257     case 'I':
258       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)__hour12(t->tm_hour));
259       __append(buf, _buf, ((long)__hour12(t->tm_hour) < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
260       break;
261 
262     case 'j':
263       _bend = __write_integer(_buf, 0, (long)((long)t->tm_yday + 1));
264       __append(buf, _buf, _bend, ct);
265       break;
266 
267     case 'm':
268       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_mon + 1);
269       __append(buf, _buf, ((long)(t->tm_mon + 1) < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
270       break;
271 
272     case 'M':
273       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_min);
274       __append(buf, _buf, ((long)t->tm_min < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
275       break;
276 
277     case 'p':
278       __append(buf, table._M_am_pm[t->tm_hour / 12]);
279       break;
280 
281     case 'S': // pad with zeros
282        _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_sec);
283        __append(buf, _buf, ((long)t->tm_sec < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
284        break;
285 
286     case 'U':
287       _bend = __write_integer(_buf, 0, long((t->tm_yday - t->tm_wday + 7) / 7));
288       __append(buf, _buf, _bend, ct);
289       break;
290 
291     case 'w':
292       _bend = __write_integer(_buf, 0, (long)t->tm_wday);
293       __append(buf, _buf, _bend, ct);
294       break;
295 
296     case 'W':
297       _bend = __write_integer(_buf, 0,
298                              (long)(t->tm_wday == 0 ? (t->tm_yday + 1) / 7 :
299                                                       (t->tm_yday + 8 - t->tm_wday) / 7));
300       __append(buf, _buf, _bend, ct);
301       break;
302 
303     case'x':
304       __subformat(buf, ct, (modifier != '#') ? table._M_date_format
305                                              : table._M_long_date_format, table, t);
306       break;
307 
308     case 'X':
309       __subformat(buf, ct, table._M_time_format, table, t);
310       break;
311 
312     case 'y':
313       _bend = __write_integer(_buf, 0, (long)((long)(t->tm_year + 1900) % 100));
314       __append(buf, _buf, _bend, ct);
315       break;
316 
317     case 'Y':
318       _bend = __write_integer(_buf, 0, (long)((long)t->tm_year + 1900));
319       __append(buf, _buf, _bend, ct);
320       break;
321 
322     case '%':
323       buf.append(1, ct.widen('%'));
324       break;
325 
326 #if defined (__GNUC__)
327       // fbp : at least on SUN
328 #  if defined (_STLP_UNIX) && !defined (__linux__)
329 #    define __USE_BSD 1
330 #  endif
331 
332    /*********************************************
333     *     JGS, handle various extensions        *
334     *********************************************/
335 
336     case 'h': /* POSIX.2 extension */
337       // same as 'b', abbrev month name
338       __append(buf, table._M_monthname[t->tm_mon]);
339       break;
340     case 'C': /* POSIX.2 extension */
341       // same as 'd', the day
342       _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_mday);
343       __append(buf, _buf, _buf + 2, ct);
344       break;
345 
346     case 'D': /* POSIX.2 extension */
347       // same as 'x'
348       __subformat(buf, ct, table._M_date_format, table, t);
349       break;
350 
351     case 'k': /* GNU extension */
352       _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_hour);
353       __append(buf, _buf, _buf + 2, ct);
354       break;
355 
356     case 'l': /* GNU extension */
357       _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_hour % 12);
358       __append(buf, _buf, _buf + 2, ct);
359       break;
360 
361     case 'n': /* POSIX.2 extension */
362       buf.append(1, ct.widen('\n'));
363       break;
364 
365     case 'R': /* GNU extension */
366       __subformat(buf, ct, "%H:%M", table, t);
367       break;
368 
369     case 'r': /* POSIX.2 extension */
370       __subformat(buf, ct, "%I:%M:%S %p", table, t);
371       break;
372 
373     case 'T': /* POSIX.2 extension.  */
374       __subformat(buf, ct, "%H:%M:%S", table, t);
375       break;
376 
377     case 't': /* POSIX.2 extension.  */
378       buf.append(1, ct.widen('\t'));
379 
380     case 'u': /* POSIX.2 extension.  */
381       _bend = __write_integer(_buf, 0, long((t->tm_wday - 1 + 7)) % 7 + 1);
382       __append(buf, _buf, _bend, ct);
383       break;
384 
385     case 's': {
386       time_t __t = mktime(__CONST_CAST(tm*, t));
387       _bend = __write_integer(_buf, 0, (long)__t );
388       __append(buf, _buf, _bend, ct);
389       break;
390     }
391     case 'g': /* GNU extension */
392     case 'G': {
393       int year = t->tm_year + __TM_YEAR_BASE;
394       int days = __iso_week_days (t->tm_yday, t->tm_wday);
395       if (days < 0) {
396         /* This ISO week belongs to the previous year.  */
397         year--;
398         days = __iso_week_days (t->tm_yday + (365 + __is_leap (year)), t->tm_wday);
399       }
400       else {
401         int d = __iso_week_days (t->tm_yday - (365 + __is_leap (year)), t->tm_wday);
402         if (0 <= d) {
403           /* This ISO week belongs to the next year.  */
404           ++year;
405           days = d;
406         }
407       }
408       long val;
409       switch (format) {
410       case 'g':
411         val = (long)(year % 100 + 100) % 100;
412         break;
413       case 'G':
414         val = (long)year;
415         break;
416       default:
417         val = (long)days / 7 + 1;
418         break;
419       }
420       _bend = __write_integer(_buf, 0, val);
421       __append(buf, _buf, _bend, ct);
422       break;
423     }
424 
425 #  if defined (_STLP_USE_GLIBC)
426     case 'z':   /* GNU extension.  */
427       if (t->tm_isdst < 0)
428         break;
429       {
430         int diff;
431 #    if defined (__USE_BSD) || defined (__BEOS__)
432         diff = t->tm_gmtoff;
433 #    else
434         diff = t->__tm_gmtoff;
435 #    endif
436         if (diff < 0) {
437           buf.append(1, ct.widen('-'));
438           diff = -diff;
439         } else
440           buf.append(1, ct.widen('+'));
441         diff /= 60;
442         _STLP_SPRINTF(_buf, "%.4d", (diff / 60) * 100 + diff % 60);
443         __append(buf, _buf, _buf + 4, ct);
444         break;
445       }
446 #  endif /* __GLIBC__ */
447 #endif /* __GNUC__ */
448 
449     default:
450       break;
451   }
452 }
453 
454 void _STLP_CALL __write_formatted_time(__iostring &buf, const ctype<char>& ct,
455                                        char format, char modifier,
456                                        const _Time_Info& table, const tm* t)
457 { __write_formatted_timeT(buf, ct, format, modifier, table, t); }
458 
459 void _STLP_CALL __write_formatted_time(__iowstring &buf, const ctype<wchar_t>& ct,
460                                        char format, char modifier,
461                                        const _WTime_Info& table, const tm* t)
462 { __write_formatted_timeT(buf, ct, format, modifier, table, t); }
463 
464 static time_base::dateorder __get_date_order(_Locale_time* time) {
465   const char * fmt = _Locale_d_fmt(time);
466   char first, second, third;
467 
468   while (*fmt != 0 && *fmt != '%') ++fmt;
469   if (*fmt == 0)
470     return time_base::no_order;
471   first = *++fmt;
472   while (*fmt != 0 && *fmt != '%') ++fmt;
473   if (*fmt == 0)
474     return time_base::no_order;
475   second = *++fmt;
476   while (*fmt != 0 && *fmt != '%') ++fmt;
477   if (*fmt == 0)
478     return time_base::no_order;
479   third = *++fmt;
480 
481   switch (first) {
482     case 'd':
483       return (second == 'm' && third == 'y') ? time_base::dmy
484                                              : time_base::no_order;
485     case 'm':
486       return (second == 'd' && third == 'y') ? time_base::mdy
487                                              : time_base::no_order;
488     case 'y':
489       switch (second) {
490         case 'd':
491           return third == 'm' ? time_base::ydm : time_base::no_order;
492         case 'm':
493           return third == 'd' ? time_base::ymd : time_base::no_order;
494         default:
495           return time_base::no_order;
496       }
497     default:
498       return time_base::no_order;
499   }
500 }
501 
502 time_init<char>::time_init()
503   : _M_dateorder(time_base::no_order)
504 { _Init_timeinfo(_M_timeinfo); }
505 
506 time_init<char>::time_init(const char* __name) {
507   if (!__name)
508     locale::_M_throw_on_null_name();
509 
510   int __err_code;
511   char buf[_Locale_MAX_SIMPLE_NAME];
512   _Locale_time *__time = __acquire_time(__name, buf, 0, &__err_code);
513   if (!__time)
514     locale::_M_throw_on_creation_failure(__err_code, __name, "time");
515 
516   _Init_timeinfo(this->_M_timeinfo, __time);
517   _M_dateorder = __get_date_order(__time);
518   __release_time(__time);
519 }
520 
521 time_init<char>::time_init(_Locale_time *__time) {
522   _Init_timeinfo(this->_M_timeinfo, __time);
523   _M_dateorder = __get_date_order(__time);
524 }
525 
526 #ifndef _STLP_NO_WCHAR_T
527 time_init<wchar_t>::time_init()
528   : _M_dateorder(time_base::no_order)
529 { _Init_timeinfo(_M_timeinfo); }
530 
531 time_init<wchar_t>::time_init(const char* __name) {
532   if (!__name)
533     locale::_M_throw_on_null_name();
534 
535   int __err_code;
536   char buf[_Locale_MAX_SIMPLE_NAME];
537   _Locale_time *__time = __acquire_time(__name, buf, 0, &__err_code);
538   if (!__time)
539     locale::_M_throw_on_creation_failure(__err_code, __name, "time");
540 
541   _Init_timeinfo(this->_M_timeinfo, __time);
542   _M_dateorder = __get_date_order(__time);
543   __release_time(__time);
544 }
545 
546 time_init<wchar_t>::time_init(_Locale_time *__time) {
547   _Init_timeinfo(this->_M_timeinfo, __time);
548   _M_dateorder = __get_date_order(__time);
549 }
550 #endif
551 
552 _STLP_MOVE_TO_STD_NAMESPACE
553 
554 #if !defined (_STLP_NO_FORCE_INSTANTIATE)
555 template class time_get<char, istreambuf_iterator<char, char_traits<char> > >;
556 template class time_put<char, ostreambuf_iterator<char, char_traits<char> > >;
557 
558 #  ifndef _STLP_NO_WCHAR_T
559 template class time_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t> > >;
560 template class time_put<wchar_t, ostreambuf_iterator<wchar_t, char_traits<wchar_t> > >;
561 #  endif
562 
563 #endif
564 
565 _STLP_END_NAMESPACE
566