1 /* 2 * COPYRIGHT: LGPL, See LGPL.txt in the top level directory 3 * PROJECT: ReactOS CRT library 4 * FILE: lib/sdk/crt/time/strftime.c 5 * PURPOSE: 6 * PROGRAMER: 7 */ 8 #include <precomp.h> 9 10 static inline BOOL strftime_date(char *str, size_t *pos, size_t max, 11 BOOL alternate, const struct tm *mstm, MSVCRT___lc_time_data *time_data) 12 { 13 char *format; 14 SYSTEMTIME st; 15 size_t ret; 16 17 st.wYear = mstm->tm_year + 1900; 18 st.wMonth = mstm->tm_mon + 1; 19 st.wDayOfWeek = mstm->tm_wday; 20 st.wDay = mstm->tm_mday; 21 st.wHour = mstm->tm_hour; 22 st.wMinute = mstm->tm_min; 23 st.wSecond = mstm->tm_sec; 24 st.wMilliseconds = 0; 25 26 format = alternate ? time_data->str.names.date : time_data->str.names.short_date; 27 ret = GetDateFormatA(time_data->lcid, 0, &st, format, NULL, 0); 28 if(ret && ret<max-*pos) 29 ret = GetDateFormatA(time_data->lcid, 0, &st, format, str+*pos, max-*pos); 30 if(!ret) { 31 *str = 0; 32 *_errno() = EINVAL; 33 return FALSE; 34 }else if(ret > max-*pos) { 35 *str = 0; 36 *_errno() = ERANGE; 37 return FALSE; 38 } 39 *pos += ret-1; 40 return TRUE; 41 } 42 43 static inline BOOL strftime_time(char *str, size_t *pos, size_t max, 44 const struct tm *mstm, MSVCRT___lc_time_data *time_data) 45 { 46 SYSTEMTIME st; 47 size_t ret; 48 49 st.wYear = mstm->tm_year + 1900; 50 st.wMonth = mstm->tm_mon + 1; 51 st.wDayOfWeek = mstm->tm_wday; 52 st.wDay = mstm->tm_mday; 53 st.wHour = mstm->tm_hour; 54 st.wMinute = mstm->tm_min; 55 st.wSecond = mstm->tm_sec; 56 st.wMilliseconds = 0; 57 58 ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time, NULL, 0); 59 if(ret && ret<max-*pos) 60 ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time, 61 str+*pos, max-*pos); 62 if(!ret) { 63 *str = 0; 64 *_errno() = EINVAL; 65 return FALSE; 66 }else if(ret > max-*pos) { 67 *str = 0; 68 *_errno() = ERANGE; 69 return FALSE; 70 } 71 *pos += ret-1; 72 return TRUE; 73 } 74 75 static inline BOOL strftime_str(char *str, size_t *pos, size_t max, char *src) 76 { 77 size_t len = strlen(src); 78 if(len > max-*pos) { 79 *str = 0; 80 *_errno() = ERANGE; 81 return FALSE; 82 } 83 84 memcpy(str+*pos, src, len); 85 *pos += len; 86 return TRUE; 87 } 88 89 static inline BOOL strftime_int(char *str, size_t *pos, size_t max, 90 int src, int prec, int l, int h) 91 { 92 size_t len; 93 94 if(src<l || src>h) { 95 *str = 0; 96 *_errno() = EINVAL; 97 return FALSE; 98 } 99 100 len = _snprintf(str+*pos, max-*pos, "%0*d", prec, src); 101 if(len == -1) { 102 *str = 0; 103 *_errno() = ERANGE; 104 return FALSE; 105 } 106 107 *pos += len; 108 return TRUE; 109 } 110 111 /********************************************************************* 112 * _Strftime (MSVCRT.@) 113 */ 114 size_t CDECL _Strftime(char *str, size_t max, const char *format, 115 const struct tm *mstm, void *_Lc_time_arg) 116 { 117 MSVCRT___lc_time_data *time_data = (MSVCRT___lc_time_data*)_Lc_time_arg; 118 size_t ret, tmp; 119 BOOL alternate; 120 121 TRACE("(%p %ld %s %p %p)\n", str, max, format, mstm, time_data); 122 123 if(!str || !format) { 124 if(str && max) 125 *str = 0; 126 *_errno() = EINVAL; 127 return 0; 128 } 129 130 if(!time_data) 131 time_data = get_locinfo()->lc_time_curr; 132 133 for(ret=0; *format && ret<max; format++) { 134 if(*format != '%') { 135 str[ret++] = *format; 136 continue; 137 } 138 139 format++; 140 if(*format == '#') { 141 alternate = TRUE; 142 format++; 143 }else { 144 alternate = FALSE; 145 } 146 147 if(!mstm) 148 goto einval_error; 149 150 switch(*format) { 151 case 'c': 152 if(!strftime_date(str, &ret, max, alternate, mstm, time_data)) 153 return 0; 154 if(ret < max) 155 str[ret++] = ' '; 156 if(!strftime_time(str, &ret, max, mstm, time_data)) 157 return 0; 158 break; 159 case 'x': 160 if(!strftime_date(str, &ret, max, alternate, mstm, time_data)) 161 return 0; 162 break; 163 case 'X': 164 if(!strftime_time(str, &ret, max, mstm, time_data)) 165 return 0; 166 break; 167 case 'a': 168 if(mstm->tm_wday<0 || mstm->tm_wday>6) 169 goto einval_error; 170 if(!strftime_str(str, &ret, max, time_data->str.names.short_wday[mstm->tm_wday])) 171 return 0; 172 break; 173 case 'A': 174 if(mstm->tm_wday<0 || mstm->tm_wday>6) 175 goto einval_error; 176 if(!strftime_str(str, &ret, max, time_data->str.names.wday[mstm->tm_wday])) 177 return 0; 178 break; 179 case 'b': 180 if(mstm->tm_mon<0 || mstm->tm_mon>11) 181 goto einval_error; 182 if(!strftime_str(str, &ret, max, time_data->str.names.short_mon[mstm->tm_mon])) 183 return 0; 184 break; 185 case 'B': 186 if(mstm->tm_mon<0 || mstm->tm_mon>11) 187 goto einval_error; 188 if(!strftime_str(str, &ret, max, time_data->str.names.mon[mstm->tm_mon])) 189 return 0; 190 break; 191 case 'd': 192 if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 0, 31)) 193 return 0; 194 break; 195 case 'H': 196 if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) 197 return 0; 198 break; 199 case 'I': 200 tmp = mstm->tm_hour; 201 if(tmp > 12) 202 tmp -= 12; 203 else if(!tmp) 204 tmp = 12; 205 if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 1, 12)) 206 return 0; 207 break; 208 case 'j': 209 if(!strftime_int(str, &ret, max, mstm->tm_yday+1, alternate ? 0 : 3, 1, 366)) 210 return 0; 211 break; 212 case 'm': 213 if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12)) 214 return 0; 215 break; 216 case 'M': 217 if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) 218 return 0; 219 break; 220 case 'p': 221 if(mstm->tm_hour<0 || mstm->tm_hour>23) 222 goto einval_error; 223 if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ? 224 time_data->str.names.am : time_data->str.names.pm)) 225 return 0; 226 break; 227 case 'S': 228 if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59)) 229 return 0; 230 break; 231 case 'w': 232 if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6)) 233 return 0; 234 break; 235 case 'y': 236 if(!strftime_int(str, &ret, max, mstm->tm_year%100, alternate ? 0 : 2, 0, 99)) 237 return 0; 238 break; 239 case 'Y': 240 tmp = 1900+mstm->tm_year; 241 if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 4, 0, 9999)) 242 return 0; 243 break; 244 case 'z': 245 case 'Z': 246 _tzset(); 247 if(_get_tzname(&tmp, str+ret, max-ret, mstm->tm_isdst ? 1 : 0)) 248 return 0; 249 ret += tmp; 250 break; 251 case 'U': 252 case 'W': 253 if(mstm->tm_wday<0 || mstm->tm_wday>6 || mstm->tm_yday<0 || mstm->tm_yday>365) 254 goto einval_error; 255 if(*format == 'U') 256 tmp = mstm->tm_wday; 257 else if(!mstm->tm_wday) 258 tmp = 6; 259 else 260 tmp = mstm->tm_wday-1; 261 262 tmp = mstm->tm_yday/7 + (tmp <= ((unsigned)mstm->tm_yday%7)); 263 if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 0, 53)) 264 return 0; 265 break; 266 case '%': 267 str[ret++] = '%'; 268 break; 269 default: 270 WARN("unknown format %c\n", *format); 271 goto einval_error; 272 } 273 } 274 275 if(ret == max) { 276 if(max) 277 *str = 0; 278 *_errno() = ERANGE; 279 return 0; 280 } 281 282 str[ret] = 0; 283 return ret; 284 285 einval_error: 286 *str = 0; 287 *_errno() = EINVAL; 288 return 0; 289 } 290 291 /********************************************************************* 292 * strftime (MSVCRT.@) 293 */ 294 size_t CDECL strftime( char *str, size_t max, const char *format, 295 const struct tm *mstm ) 296 { 297 return _Strftime(str, max, format, mstm, NULL); 298 } 299 300 /********************************************************************* 301 * wcsftime (MSVCRT.@) 302 */ 303 size_t CDECL wcsftime( wchar_t *str, size_t max, 304 const wchar_t *format, const struct tm *mstm ) 305 { 306 char *s, *fmt; 307 size_t len; 308 309 TRACE("%p %ld %s %p\n", str, max, debugstr_w(format), mstm ); 310 311 len = WideCharToMultiByte( CP_ACP, 0, format, -1, NULL, 0, NULL, NULL ); 312 if (!(fmt = malloc( len ))) return 0; 313 WideCharToMultiByte( CP_ACP, 0, format, -1, fmt, len, NULL, NULL ); 314 315 if ((s = malloc( max*4 ))) 316 { 317 if (!strftime( s, max*4, fmt, mstm )) s[0] = 0; 318 len = MultiByteToWideChar( CP_ACP, 0, s, -1, str, max ); 319 if (len) len--; 320 free( s ); 321 } 322 else len = 0; 323 324 free( fmt ); 325 return len; 326 } 327