]> git.saurik.com Git - wxWidgets.git/blame - src/msw/wince/time.cpp
supress unused var warning
[wxWidgets.git] / src / msw / wince / time.cpp
CommitLineData
1c193821
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/wince/time.cpp
3// Purpose: Implements missing time functionality for WinCE
f22cc0b4
JS
4// Author: Marco Cavallini (MCK) - wx@koansoftware.com
5// Modified by:
6// Created: 31-08-2003
7// RCS-ID:
991420e6 8// Copyright: (c) Marco Cavallini
65571936 9// Licence: wxWindows licence
1c193821
JS
10/////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
1c193821
JS
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
27#ifndef WX_PRECOMP
9ed0d735 28 #include "wx/msw/wrapwin.h"
1c193821
JS
29#endif
30
31#include "wx/msw/wince/time.h"
553d7089
JS
32
33/////////////////////////////////////////////////////////////////////////////////////////////
34// //
35// strftime() - taken from OpenBSD //
36// //
37/////////////////////////////////////////////////////////////////////////////////////////////
38
39#define IN_NONE 0
40#define IN_SOME 1
41#define IN_THIS 2
42#define IN_ALL 3
43#define CHAR_BIT 8
44
45#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
46#define TYPE_SIGNED(type) (((type) -1) < 0)
47
48#define INT_STRLEN_MAXIMUM(type) \
49 ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
50
51#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
52
53#define MONSPERYEAR 12
54#define DAYSPERWEEK 7
55#define TM_YEAR_BASE 1900
56#define HOURSPERDAY 24
57#define DAYSPERNYEAR 365
58#define DAYSPERLYEAR 366
59
60static char wildabbr[] = "WILDABBR";
61
62char * tzname[2] = {
63 wildabbr,
64 wildabbr
65};
66
67
68#define Locale (&C_time_locale)
69
70struct lc_time_T {
71 const char * mon[MONSPERYEAR];
72 const char * month[MONSPERYEAR];
73 const char * wday[DAYSPERWEEK];
74 const char * weekday[DAYSPERWEEK];
75 const char * X_fmt;
76 const char * x_fmt;
77 const char * c_fmt;
78 const char * am;
79 const char * pm;
80 const char * date_fmt;
81};
82
83static const struct lc_time_T C_time_locale = {
84 {
85 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
86 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
87 }, {
88 "January", "February", "March", "April", "May", "June",
89 "July", "August", "September", "October", "November", "December"
90 }, {
91 "Sun", "Mon", "Tue", "Wed",
92 "Thu", "Fri", "Sat"
93 }, {
94 "Sunday", "Monday", "Tuesday", "Wednesday",
95 "Thursday", "Friday", "Saturday"
96 },
97
98 /* X_fmt */
99 "%H:%M:%S",
100
101 /*
102 ** x_fmt
103 ** C99 requires this format.
104 ** Using just numbers (as here) makes Quakers happier;
105 ** it's also compatible with SVR4.
106 */
107 "%m/%d/%y",
108
109 /*
110 ** c_fmt
111 ** C99 requires this format.
112 ** Previously this code used "%D %X", but we now conform to C99.
113 ** Note that
114 ** "%a %b %d %H:%M:%S %Y"
115 ** is used by Solaris 2.3.
116 */
117 "%a %b %e %T %Y",
118
119 /* am */
120 "AM",
121
122 /* pm */
123 "PM",
124
125 /* date_fmt */
126 "%a %b %e %H:%M:%S %Z %Y"
127};
128
129
130static char *
131_add(const char * str, char * pt, const char * const ptlim)
132{
133 while (pt < ptlim && (*pt = *str++) != '\0')
134 ++pt;
135 return pt;
136}
137
138
139static char *
140_conv(const int n, const char * const format, char * const pt, const char * const ptlim)
141{
142 char buf[INT_STRLEN_MAXIMUM(int) + 1];
143
144 (void) _snprintf(buf, sizeof buf, format, n);
145 return _add(buf, pt, ptlim);
146}
147
148
149static char *
150_fmt(const char * format, const struct tm * const t, char * pt, const char * const ptlim, int * warnp)
151{
152 for ( ; *format; ++format) {
153 if (*format == '%') {
154label:
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 = _conv((t->tm_year + TM_YEAR_BASE) / 100,
193 "%02d", pt, ptlim);
194 continue;
195 case 'c':
196 {
197 int warn2 = IN_SOME;
198
199 pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
200 if (warn2 == IN_ALL)
201 warn2 = IN_THIS;
202 if (warn2 > *warnp)
203 *warnp = warn2;
204 }
205 continue;
206 case 'D':
207 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
208 continue;
209 case 'd':
210 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
211 continue;
212 case 'E':
213 case 'O':
214 /*
215 ** C99 locale modifiers.
216 ** The sequences
217 ** %Ec %EC %Ex %EX %Ey %EY
218 ** %Od %oe %OH %OI %Om %OM
219 ** %OS %Ou %OU %OV %Ow %OW %Oy
220 ** are supposed to provide alternate
221 ** representations.
222 */
223 goto label;
224 case 'e':
225 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
226 continue;
227 case 'F':
228 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
229 continue;
230 case 'H':
231 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
232 continue;
233 case 'I':
234 pt = _conv((t->tm_hour % 12) ?
235 (t->tm_hour % 12) : 12,
236 "%02d", pt, ptlim);
237 continue;
238 case 'j':
239 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
240 continue;
241 case 'k':
242 /*
243 ** This used to be...
244 ** _conv(t->tm_hour % 12 ?
245 ** t->tm_hour % 12 : 12, 2, ' ');
246 ** ...and has been changed to the below to
247 ** match SunOS 4.1.1 and Arnold Robbins'
248 ** strftime version 3.0. That is, "%k" and
249 ** "%l" have been swapped.
250 ** (ado, 1993-05-24)
251 */
252 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
253 continue;
254#ifdef KITCHEN_SINK
255 case 'K':
256 /*
257 ** After all this time, still unclaimed!
258 */
259 pt = _add("kitchen sink", pt, ptlim);
260 continue;
261#endif /* defined KITCHEN_SINK */
262 case 'l':
263 /*
264 ** This used to be...
265 ** _conv(t->tm_hour, 2, ' ');
266 ** ...and has been changed to the below to
267 ** match SunOS 4.1.1 and Arnold Robbin's
268 ** strftime version 3.0. That is, "%k" and
269 ** "%l" have been swapped.
270 ** (ado, 1993-05-24)
271 */
272 pt = _conv((t->tm_hour % 12) ?
273 (t->tm_hour % 12) : 12,
274 "%2d", pt, ptlim);
275 continue;
276 case 'M':
277 pt = _conv(t->tm_min, "%02d", pt, ptlim);
278 continue;
279 case 'm':
280 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
281 continue;
282 case 'n':
283 pt = _add("\n", pt, ptlim);
284 continue;
285 case 'p':
286 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
287 Locale->pm :
288 Locale->am,
289 pt, ptlim);
290 continue;
291 case 'R':
292 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
293 continue;
294 case 'r':
295 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
296 continue;
297 case 'S':
298 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
299 continue;
300 case 's':
301 {
302 struct tm tm;
303 char buf[INT_STRLEN_MAXIMUM(
304 time_t) + 1];
305 time_t mkt;
306
307 tm = *t;
308 mkt = mktime(&tm);
309 if (TYPE_SIGNED(time_t))
310 (void) _snprintf(buf, sizeof buf,
311 "%ld", (long) mkt);
312 else (void) _snprintf(buf, sizeof buf,
313 "%lu", (unsigned long) mkt);
314 pt = _add(buf, pt, ptlim);
315 }
316 continue;
317 case 'T':
318 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
319 continue;
320 case 't':
321 pt = _add("\t", pt, ptlim);
322 continue;
323 case 'U':
324 pt = _conv((t->tm_yday + DAYSPERWEEK -
325 t->tm_wday) / DAYSPERWEEK,
326 "%02d", pt, ptlim);
327 continue;
328 case 'u':
329 /*
330 ** From Arnold Robbins' strftime version 3.0:
331 ** "ISO 8601: Weekday as a decimal number
332 ** [1 (Monday) - 7]"
333 ** (ado, 1993-05-24)
334 */
335 pt = _conv((t->tm_wday == 0) ?
336 DAYSPERWEEK : t->tm_wday,
337 "%d", pt, ptlim);
338 continue;
339 case 'V': /* ISO 8601 week number */
340 case 'G': /* ISO 8601 year (four digits) */
341 case 'g': /* ISO 8601 year (two digits) */
342 {
343 int year;
344 int yday;
345 int wday;
346 int w;
347
348 year = t->tm_year + TM_YEAR_BASE;
349 yday = t->tm_yday;
350 wday = t->tm_wday;
351 for ( ; ; ) {
352 int len;
353 int bot;
354 int top;
355
356 len = isleap(year) ?
357 DAYSPERLYEAR :
358 DAYSPERNYEAR;
359 /*
360 ** What yday (-3 ... 3) does
361 ** the ISO year begin on?
362 */
363 bot = ((yday + 11 - wday) %
364 DAYSPERWEEK) - 3;
365 /*
366 ** What yday does the NEXT
367 ** ISO year begin on?
368 */
369 top = bot -
370 (len % DAYSPERWEEK);
371 if (top < -3)
372 top += DAYSPERWEEK;
373 top += len;
374 if (yday >= top) {
375 ++year;
376 w = 1;
377 break;
378 }
379 if (yday >= bot) {
380 w = 1 + ((yday - bot) /
381 DAYSPERWEEK);
382 break;
383 }
384 --year;
385 yday += isleap(year) ?
386 DAYSPERLYEAR :
387 DAYSPERNYEAR;
388 }
389 if (*format == 'V')
390 pt = _conv(w, "%02d",
391 pt, ptlim);
392 else if (*format == 'g') {
393 *warnp = IN_ALL;
394 pt = _conv(year % 100, "%02d",
395 pt, ptlim);
396 } else pt = _conv(year, "%04d",
397 pt, ptlim);
398 }
399 continue;
400 case 'v':
401 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
402 continue;
403 case 'W':
404 pt = _conv((t->tm_yday + DAYSPERWEEK -
405 (t->tm_wday ?
406 (t->tm_wday - 1) :
407 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
408 "%02d", pt, ptlim);
409 continue;
410 case 'w':
411 pt = _conv(t->tm_wday, "%d", pt, ptlim);
412 continue;
413 case 'X':
414 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
415 continue;
416 case 'x':
417 {
418 int warn2 = IN_SOME;
419
420 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
421 if (warn2 == IN_ALL)
422 warn2 = IN_THIS;
423 if (warn2 > *warnp)
424 *warnp = warn2;
425 }
426 continue;
427 case 'y':
428 *warnp = IN_ALL;
429 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
430 "%02d", pt, ptlim);
431 continue;
432 case 'Y':
433 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
434 pt, ptlim);
435 continue;
436 case 'Z':
437 if (t->tm_isdst >= 0)
438 pt = _add(tzname[t->tm_isdst != 0],
439 pt, ptlim);
440 /*
441 ** C99 says that %Z must be replaced by the
442 ** empty string if the time zone is not
443 ** determinable.
444 */
445 continue;
446 case 'z':
447 {
448 int diff;
449 char const * sign;
450
451 if (t->tm_isdst < 0)
452 continue;
453 continue;
454 if (diff < 0) {
455 sign = "-";
456 diff = -diff;
457 } else sign = "+";
458 pt = _add(sign, pt, ptlim);
459 diff /= 60;
460 pt = _conv((diff/60)*100 + diff%60,
461 "%04d", pt, ptlim);
462 }
463 continue;
464 case '+':
465 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
466 warnp);
467 continue;
468 case '%':
469 default:
470 break;
471 }
472 }
473 if (pt == ptlim)
474 break;
475 *pt++ = *format;
476 }
477 return pt;
478}
479
480size_t
481strftime(char * const s, const size_t maxsize, const char *format, const struct tm * const t)
482{
483 char * p;
484 int warn;
485
486 //tzset();
487
488 warn = IN_NONE;
489 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
490
491 if (p == s + maxsize) {
492 if (maxsize > 0)
493 s[maxsize - 1] = '\0';
494 return 0;
495 }
496 *p = '\0';
497 return p - s;
498}
499
500extern "C"
501{
502
503size_t wcsftime(wchar_t* const s, const size_t maxsize, const wchar_t *format, const struct tm * const t)
504{
505 char sBuf[256];
506 sBuf[0] = 0;
507
508 wxString formatStr(format);
509 wxCharBuffer bufFormatStr(formatStr.mb_str());
510
511 size_t sz = strftime(sBuf, maxsize, bufFormatStr, t);
512
513 wxMB2WC(s, sBuf, strlen(sBuf));
514
515 return sz;
516}
517
518} /* extern "C" */
1c193821 519
f22cc0b4 520#define is_leap(y) (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
991420e6
WS
521#define SECONDS_IN_ONE_MINUTE 60
522#define DAYS_IN_ONE_YEAR 365
523#define SECONDS_IN_ONE_MIN 60
524#define SECONDS_IN_ONE_HOUR 3600
525#define SECONDS_IN_ONE_DAY 86400
526#define DEFAULT_TIMEZONE 28800
527#define DO_GMTIME 0
528#define DO_LOCALTIME 1
1c193821 529
1c193821 530
991420e6 531long timezone ; // global variable
f22cc0b4
JS
532
533
534////////////////////////////////////////////////////////////////////////
535// Common code for localtime and gmtime (static)
536////////////////////////////////////////////////////////////////////////
cff614f6
JS
537
538static struct tm * __cdecl common_localtime(const time_t *t, BOOL bLocal)
1c193821 539{
cff614f6
JS
540 wxLongLong i64;
541 FILETIME ft;
991420e6
WS
542 wxString str ;
543 SYSTEMTIME SystemTime;
544 TIME_ZONE_INFORMATION pTz;
545 static struct tm st_res ; // data holder
546 static struct tm * res = &st_res ; // data pointer
547 int iLeap;
548 const unsigned short int __mon_yday[2][13] =
549 {
550 // Normal years
551 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
552 // Leap years
553 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
554 };
555
cff614f6
JS
556 if (!*t)
557 ::GetLocalTime(&SystemTime);
558 else
559 {
560 i64 = *t;
561 i64 = i64 * 10000000 + 116444736000000000;
562
563 ft.dwLowDateTime = i64.GetLo();
564 ft.dwHighDateTime = i64.GetHi();
565
566 ::FileTimeToSystemTime(&ft, &SystemTime);
567 }
568
991420e6
WS
569 ::GetTimeZoneInformation(&pTz);
570
571 ///////////////////////////////////////////////
572 // Set timezone
573 timezone = pTz.Bias * SECONDS_IN_ONE_MINUTE ;
574 ///////////////////////////////////////////////
575
576 iLeap = is_leap(SystemTime.wYear) ;
577
578 res->tm_hour = SystemTime.wHour;
579 res->tm_min = SystemTime.wMinute;
580 res->tm_sec = SystemTime.wSecond;
581
582 res->tm_mday = SystemTime.wDay;
583 res->tm_mon = SystemTime.wMonth - 1; // this the correct month but localtime returns month aligned to zero
584 res->tm_year = SystemTime.wYear; // this the correct year
585 res->tm_year = res->tm_year - 1900; // but localtime returns the value starting at the 1900
586
587 res->tm_wday = SystemTime.wDayOfWeek;
588 res->tm_yday = __mon_yday[iLeap][res->tm_mon] + SystemTime.wDay - 1; // localtime returns year-day aligned to zero
589
590 // if localtime behavior and daylight saving
591 if (bLocal && pTz.DaylightBias != 0)
592 res->tm_isdst = 1;
593 else
594 res->tm_isdst = 0; // without daylight saving or gmtime
f22cc0b4
JS
595
596 return res;
1c193821
JS
597}
598
92bdf9f7
VZ
599extern "C"
600{
601
f22cc0b4
JS
602////////////////////////////////////////////////////////////////////////
603// Receive the number of seconds elapsed since midnight(00:00:00)
604// and convert a time value and corrects for the local time zone
605////////////////////////////////////////////////////////////////////////
606struct tm * __cdecl localtime(const time_t * t)
1c193821 607{
991420e6 608 return common_localtime(t, DO_LOCALTIME) ;
1c193821
JS
609}
610
f22cc0b4
JS
611////////////////////////////////////////////////////////////////////////
612// Receives the number of seconds elapsed since midnight(00:00:00)
613// and converts a time value WITHOUT correcting for the local time zone
614////////////////////////////////////////////////////////////////////////
615struct tm * __cdecl gmtime(const time_t *t)
1c193821 616{
991420e6 617 return common_localtime(t, DO_GMTIME) ;
1c193821
JS
618}
619
92bdf9f7 620}
f22cc0b4
JS
621
622////////////////////////////////////////////////////////////////////////
623// Common code for conversion of struct tm into time_t (static)
624////////////////////////////////////////////////////////////////////////
625static time_t __cdecl common_tm_to_time(int day, int month, int year, int hour, int minute, int second)
1c193821 626{
553d7089
JS
627#if 1
628 // Use mktime since it seems less broken
629 tm t;
630 t.tm_isdst = -1;
631 t.tm_hour = hour;
632 t.tm_mday = day;
633 t.tm_min = minute;
634 t.tm_mon = month-1;
635 t.tm_sec = second;
636 t.tm_wday = -1;
637 t.tm_yday = -1;
638 t.tm_year = year - 1900;
639 return mktime(& t);
640#else
991420e6
WS
641 time_t prog = 0 ;
642 static int mdays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 } ;
643
644 while (--month)
645 {
646 prog += mdays[month - 1] ;
647 if (month == 2 && is_leap(year))
648 prog++ ;
649 }
650
651 // Calculate seconds in elapsed days
652 prog = day - 1 ; // align first day of the year to zero
653 prog += (DAYS_IN_ONE_YEAR * (year - 1970) + (year - 1901) / 4 - 19) ;
654 prog *= SECONDS_IN_ONE_DAY ;
655
656 // Add Calculated elapsed seconds in the current day
657 prog += (hour * SECONDS_IN_ONE_HOUR + minute *
658 SECONDS_IN_ONE_MIN + second) ;
659
660 return prog ;
553d7089 661#endif
1c193821
JS
662}
663
92bdf9f7
VZ
664extern "C"
665{
f22cc0b4
JS
666
667////////////////////////////////////////////////////////////////////////
668// Returns the number of seconds elapsed since
669// midnight(00:00:00) of 1 January 1970
670////////////////////////////////////////////////////////////////////////
671time_t __cdecl time(time_t *t)
1c193821 672{
991420e6 673 time_t prog = 0 ;
f22cc0b4 674
991420e6
WS
675 if (t != NULL)
676 {
677 SYSTEMTIME SystemTime;
f22cc0b4 678
991420e6
WS
679 ::GetLocalTime(&SystemTime) ;
680 prog = common_tm_to_time(SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear,
681 SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond) ;
682 *t = prog ;
683 }
f22cc0b4 684
991420e6 685 return prog ;
1c193821
JS
686}
687
f22cc0b4
JS
688////////////////////////////////////////////////////////////////////////
689// Converts the local time provided by struct tm
690// into a time_t calendar value
553d7089 691// Implementation from OpenBSD
f22cc0b4 692////////////////////////////////////////////////////////////////////////
f22cc0b4 693
553d7089
JS
694#if 1
695int month_to_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
f22cc0b4 696
553d7089 697time_t mktime(struct tm *t)
f22cc0b4 698{
553d7089
JS
699 short month, year;
700 time_t result;
701
702 month = t->tm_mon;
703 year = t->tm_year + month / 12 + 1900;
704 month %= 12;
705 if (month < 0)
706 {
707 year -= 1;
708 month += 12;
709 }
710 result = (year - 1970) * 365 + (year - 1969) / 4 + month_to_day[month];
711 result = (year - 1970) * 365 + month_to_day[month];
712 if (month <= 1)
713 year -= 1;
714 result += (year - 1968) / 4;
715 result -= (year - 1900) / 100;
716 result += (year - 1600) / 400;
717 result += t->tm_mday;
718 result -= 1;
719 result *= 24;
720 result += t->tm_hour;
721 result *= 60;
722 result += t->tm_min;
723 result *= 60;
724 result += t->tm_sec;
725 return(result);
726}
f22cc0b4 727
553d7089
JS
728#else
729time_t __cdecl mktime(struct tm *t)
730{
731 return (common_tm_to_time(t->tm_mday, t->tm_mon+1, t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec)) ;
f22cc0b4 732}
553d7089 733#endif
92bdf9f7
VZ
734
735} // extern "C"
553d7089
JS
736
737