c5c65dff78cf7251d869bf4e6f7c5ae9663ea740
[reactos.git] / reactos / lib / crtdll / time / strftime.c
1 /* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
2 #include <crtdll/string.h>
3 #include <crtdll/time.h>
4 #include <crtdll/stdlib.h>
5 #include <crtdll/wchar.h>
6
7 #define TM_YEAR_BASE 1900
8
9 static const char *afmt[] = {
10 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
11 };
12 static const char *Afmt[] = {
13 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
14 "Saturday",
15 };
16 static const char *bfmt[] = {
17 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
18 "Oct", "Nov", "Dec",
19 };
20 static const char *Bfmt[] = {
21 "January", "February", "March", "April", "May", "June", "July",
22 "August", "September", "October", "November", "December",
23 };
24
25 static size_t gsize;
26 static char *pt;
27
28 static int
29 _add(const char *str)
30 {
31 for (;; ++pt, --gsize)
32 {
33 if (!gsize)
34 return 0;
35 if (!(*pt = *str++))
36 return 1;
37 }
38 }
39
40 static int
41 _conv(int n, int digits, char pad)
42 {
43 static char buf[10];
44 char *p;
45
46 for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
47 *p-- = n % 10 + '0';
48 while (p > buf && digits-- > 0)
49 *p-- = pad;
50 return _add(++p);
51 }
52
53 static size_t
54 _fmt(const char *format, const struct tm *t)
55 {
56 for (; *format; ++format)
57 {
58 if (*format == '%') {
59 if (*(format+1) == '#' ) {format++;}
60
61 switch(*++format)
62 {
63 case '\0':
64 --format;
65 break;
66 case 'A':
67 if (t->tm_wday < 0 || t->tm_wday > 6)
68 return 0;
69 if (!_add(Afmt[t->tm_wday]))
70 return 0;
71 continue;
72 case 'a':
73 if (t->tm_wday < 0 || t->tm_wday > 6)
74 return 0;
75 if (!_add(afmt[t->tm_wday]))
76 return 0;
77 continue;
78 case 'B':
79 if (t->tm_mon < 0 || t->tm_mon > 11)
80 return 0;
81 if (!_add(Bfmt[t->tm_mon]))
82 return 0;
83 continue;
84 case 'b':
85 case 'h':
86 if (t->tm_mon < 0 || t->tm_mon > 11)
87 return 0;
88 if (!_add(bfmt[t->tm_mon]))
89 return 0;
90 continue;
91 case 'C':
92 if (!_fmt("%a %b %e %H:%M:%S %Y", t))
93 return 0;
94 continue;
95 case 'c':
96 if (!_fmt("%m/%d/%y %H:%M:%S", t))
97 return 0;
98 continue;
99 case 'e':
100 if (!_conv(t->tm_mday, 2, ' '))
101 return 0;
102 continue;
103 case 'D':
104 if (!_fmt("%m/%d/%y", t))
105 return 0;
106 continue;
107 case 'd':
108 if (!_conv(t->tm_mday, 2, '0'))
109 return 0;
110 continue;
111 case 'H':
112 if (!_conv(t->tm_hour, 2, '0'))
113 return 0;
114 continue;
115 case 'I':
116 if (!_conv(t->tm_hour % 12 ?
117 t->tm_hour % 12 : 12, 2, '0'))
118 return 0;
119 continue;
120 case 'j':
121 if (!_conv(t->tm_yday + 1, 3, '0'))
122 return 0;
123 continue;
124 case 'k':
125 if (!_conv(t->tm_hour, 2, ' '))
126 return 0;
127 continue;
128 case 'l':
129 if (!_conv(t->tm_hour % 12 ?
130 t->tm_hour % 12 : 12, 2, ' '))
131 return 0;
132 continue;
133 case 'M':
134 if (!_conv(t->tm_min, 2, '0'))
135 return 0;
136 continue;
137 case 'm':
138 if (!_conv(t->tm_mon + 1, 2, '0'))
139 return 0;
140 continue;
141 case 'n':
142 if (!_add("\n"))
143 return 0;
144 continue;
145 case 'p':
146 if (!_add(t->tm_hour >= 12 ? "PM" : "AM"))
147 return 0;
148 continue;
149 case 'R':
150 if (!_fmt("%H:%M", t))
151 return 0;
152 continue;
153 case 'r':
154 if (!_fmt("%I:%M:%S %p", t))
155 return 0;
156 continue;
157 case 'S':
158 if (!_conv(t->tm_sec, 2, '0'))
159 return 0;
160 continue;
161 case 'T':
162 case 'X':
163 if (!_fmt("%H:%M:%S", t))
164 return 0;
165 continue;
166 case 't':
167 if (!_add("\t"))
168 return 0;
169 continue;
170 case 'U':
171 if (!_conv((t->tm_yday + 7 - t->tm_wday) / 7,
172 2, '0'))
173 return 0;
174 continue;
175 case 'W':
176 if (!_conv((t->tm_yday + 7 -
177 (t->tm_wday ? (t->tm_wday - 1) : 6))
178 / 7, 2, '0'))
179 return 0;
180 continue;
181 case 'w':
182 if (!_conv(t->tm_wday, 1, '0'))
183 return 0;
184 continue;
185 case 'x':
186 if (!_fmt("%m/%d/%y", t))
187 return 0;
188 continue;
189 case 'y':
190 if (!_conv((t->tm_year + TM_YEAR_BASE)
191 % 100, 2, '0'))
192 return 0;
193 continue;
194 case 'Y':
195 if (!_conv(t->tm_year + TM_YEAR_BASE, 4, '0'))
196 return 0;
197 continue;
198 case 'Z':
199 if (!t->tm_zone || !_add(t->tm_zone))
200 return 0;
201 continue;
202 case '%':
203 /*
204 * X311J/88-090 (4.12.3.5): if conversion char is
205 * undefined, behavior is undefined. Print out the
206 * character itself as printf(3) does.
207 */
208 default:
209 break;
210 }
211 }
212 if (!gsize--)
213 return 0;
214 *pt++ = *format;
215 }
216 return gsize;
217 }
218
219 size_t
220 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
221 {
222 pt = s;
223 if ((gsize = maxsize) < 1)
224 return 0;
225 if (_fmt(format, t))
226 {
227 *pt = '\0';
228 return maxsize - gsize;
229 }
230 return 0;
231 }
232
233 size_t
234 wcsftime(wchar_t *s, size_t maxsize, const wchar_t *format, const struct tm *t)
235 {
236 char *x;
237 char *f;
238 int i,j;
239 x = malloc(maxsize);
240 j = wcslen(format);
241 f = malloc(j+1);
242 for(i=0;i<j;i++)
243 f[i] = (char)*format;
244 f[i] = 0;
245 pt = x;
246 if ((gsize = maxsize) < 1)
247 return 0;
248 if (_fmt(f, t))
249 {
250 *pt = '\0';
251 free(f);
252 for(i=0;i<maxsize;i++)
253 s[i] = (wchar_t)x[i];
254 s[i] = 0;
255 free(x);
256 return maxsize - gsize;
257 }
258 for(i=0;i<maxsize;i++)
259 s[i] = (wchar_t)x[i];
260 s[i] = 0;
261 free(f);
262 free(x);
263 return 0;
264 }