1 /* $NetBSD: strftime.c,v 1.3 1997/07/13 20:26:52 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1989 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #if defined(LIBC_SCCS) && !defined(lint) 38 #if 0 39 static char *sccsid = "@(#)strftime.c 5.11 (Berkeley) 2/24/91"; 40 #else 41 __RCSID("$NetBSD: strftime.c,v 1.3 1997/07/13 20:26:52 christos Exp $"); 42 #endif 43 #endif /* LIBC_SCCS and not lint */ 44 45 #include <sys/localedef.h> 46 #include <locale.h> 47 #include <string.h> 48 #include <tzfile.h> 49 #include <time.h> 50 51 static size_t gsize; 52 static char *pt; 53 54 static int _add __P((const char *)); 55 static int _conv __P((int, int, char)); 56 static int _secs __P((const struct tm *)); 57 static size_t _fmt __P((const char *, const struct tm *)); 58 59 size_t 60 strftime(s, maxsize, format, t) 61 char *s; 62 size_t maxsize; 63 const char *format; 64 const struct tm *t; 65 { 66 tzset(); 67 68 pt = s; 69 if ((gsize = maxsize) < 1) 70 return (0); 71 if (_fmt(format, t)) { 72 *pt = '\0'; 73 return (maxsize - gsize); 74 } 75 return (0); 76 } 77 78 #define SUN_WEEK(t) (((t)->tm_yday + 7 - \ 79 ((t)->tm_wday)) / 7) 80 #define MON_WEEK(t) (((t)->tm_yday + 7 - \ 81 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) 82 static size_t 83 _fmt(format, t) 84 register const char *format; 85 const struct tm *t; 86 { 87 for (; *format; ++format) { 88 if (*format == '%') { 89 ++format; 90 if (*format == 'E') { 91 /* Alternate Era */ 92 ++format; 93 } else if (*format == 'O') { 94 /* Alternate numeric symbols */ 95 ++format; 96 } 97 switch (*format) { 98 case '\0': 99 --format; 100 break; 101 case 'A': 102 if (t->tm_wday < 0 || t->tm_wday > 6) 103 return (0); 104 if (!_add(_CurrentTimeLocale->day[t->tm_wday])) 105 return (0); 106 continue; 107 case 'a': 108 if (t->tm_wday < 0 || t->tm_wday > 6) 109 return (0); 110 if (!_add(_CurrentTimeLocale->abday[t->tm_wday])) 111 return (0); 112 continue; 113 case 'B': 114 if (t->tm_mon < 0 || t->tm_mon > 11) 115 return (0); 116 if (!_add(_CurrentTimeLocale->mon[t->tm_mon])) 117 return (0); 118 continue; 119 case 'b': 120 case 'h': 121 if (t->tm_mon < 0 || t->tm_mon > 11) 122 return (0); 123 if (!_add(_CurrentTimeLocale->abmon[t->tm_mon])) 124 return (0); 125 continue; 126 case 'C': 127 if (!_conv((t->tm_year + TM_YEAR_BASE) / 100, 128 2, '0')) 129 return (0); 130 continue; 131 case 'c': 132 if (!_fmt(_CurrentTimeLocale->d_t_fmt, t)) 133 return (0); 134 continue; 135 case 'D': 136 if (!_fmt("%m/%d/%y", t)) 137 return (0); 138 continue; 139 case 'd': 140 if (!_conv(t->tm_mday, 2, '0')) 141 return (0); 142 continue; 143 case 'e': 144 if (!_conv(t->tm_mday, 2, ' ')) 145 return (0); 146 continue; 147 case 'H': 148 if (!_conv(t->tm_hour, 2, '0')) 149 return (0); 150 continue; 151 case 'I': 152 if (!_conv(t->tm_hour % 12 ? 153 t->tm_hour % 12 : 12, 2, '0')) 154 return (0); 155 continue; 156 case 'j': 157 if (!_conv(t->tm_yday + 1, 3, '0')) 158 return (0); 159 continue; 160 case 'k': 161 if (!_conv(t->tm_hour, 2, ' ')) 162 return (0); 163 continue; 164 case 'l': 165 if (!_conv(t->tm_hour % 12 ? 166 t->tm_hour % 12: 12, 2, ' ')) 167 return (0); 168 continue; 169 case 'M': 170 if (!_conv(t->tm_min, 2, '0')) 171 return (0); 172 continue; 173 case 'm': 174 if (!_conv(t->tm_mon + 1, 2, '0')) 175 return (0); 176 continue; 177 case 'n': 178 if (!_add("\n")) 179 return (0); 180 continue; 181 case 'p': 182 if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour >= 12])) 183 return (0); 184 continue; 185 case 'R': 186 if (!_fmt("%H:%M", t)) 187 return (0); 188 continue; 189 case 'r': 190 if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t)) 191 return (0); 192 continue; 193 case 'S': 194 if (!_conv(t->tm_sec, 2, '0')) 195 return (0); 196 continue; 197 case 's': 198 if (!_secs(t)) 199 return (0); 200 continue; 201 case 'T': 202 if (!_fmt("%H:%M:%S", t)) 203 return (0); 204 continue; 205 case 't': 206 if (!_add("\t")) 207 return (0); 208 continue; 209 case 'U': 210 if (!_conv(SUN_WEEK(t), 2, '0')) 211 return (0); 212 continue; 213 case 'u': 214 if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0')) 215 return (0); 216 continue; 217 case 'V': 218 { 219 /* ISO 8601 Week Of Year: 220 * If the week (Monday - Sunday) containing 221 * January 1 has four or more days in the new 222 * year, then it is week 1; otherwise it is 223 * week 53 of the previous year and the next 224 * week is week one. 225 */ 226 227 int week = MON_WEEK(t); 228 229 int days = (((t)->tm_yday + 7 - 230 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) % 7); 231 232 233 if (days >= 4) { 234 week++; 235 } else if (week == 0) { 236 week = 53; 237 } 238 239 if (!_conv(week, 2, '0')) 240 return (0); 241 continue; 242 } 243 case 'W': 244 if (!_conv(MON_WEEK(t), 2, '0')) 245 return (0); 246 continue; 247 case 'w': 248 if (!_conv(t->tm_wday, 1, '0')) 249 return (0); 250 continue; 251 case 'x': 252 if (!_fmt(_CurrentTimeLocale->d_fmt, t)) 253 return (0); 254 continue; 255 case 'X': 256 if (!_fmt(_CurrentTimeLocale->t_fmt, t)) 257 return (0); 258 continue; 259 case 'y': 260 if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, 261 2, '0')) 262 return (0); 263 continue; 264 case 'Y': 265 if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0')) 266 return (0); 267 continue; 268 case 'Z': 269 if (tzname[t->tm_isdst ? 1 : 0] && 270 !_add(tzname[t->tm_isdst ? 1 : 0])) 271 return (0); 272 continue; 273 case '%': 274 /* 275 * X311J/88-090 (4.12.3.5): if conversion char is 276 * undefined, behavior is undefined. Print out the 277 * character itself as printf(3) does. 278 */ 279 default: 280 break; 281 } 282 } 283 if (!gsize--) 284 return (0); 285 *pt++ = *format; 286 } 287 return (gsize); 288 } 289 290 static int 291 _secs(t) 292 const struct tm *t; 293 { 294 static char buf[15]; 295 register time_t s; 296 register char *p; 297 struct tm tmp; 298 299 /* Make a copy, mktime(3) modifies the tm struct. */ 300 tmp = *t; 301 s = mktime(&tmp); 302 for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10) 303 *p-- = s % 10 + '0'; 304 return (_add(++p)); 305 } 306 307 static int 308 _conv(n, digits, pad) 309 int n, digits; 310 char pad; 311 { 312 static char buf[10]; 313 register char *p; 314 315 for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits) 316 *p-- = n % 10 + '0'; 317 while (p > buf && digits-- > 0) 318 *p-- = pad; 319 return (_add(++p)); 320 } 321 322 static int 323 _add(str) 324 register const char *str; 325 { 326 for (;; ++pt, --gsize) { 327 if (!gsize) 328 return (0); 329 if (!(*pt = *str++)) 330 return (1); 331 } 332 } 333