1 /* $NetBSD: strftime.c,v 1.4 1997/07/21 14:09:22 jtc 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.4 1997/07/21 14:09:22 jtc Exp $"); 42 #endif 43 #endif /* LIBC_SCCS and not lint */ 44 45 #include "namespace.h" 46 #include <sys/localedef.h> 47 #include <locale.h> 48 #include <string.h> 49 #include <tzfile.h> 50 #include <time.h> 51 52 static size_t gsize; 53 static char *pt; 54 55 static int _add __P((const char *)); 56 static int _conv __P((int, int, char)); 57 static int _secs __P((const struct tm *)); 58 static size_t _fmt __P((const char *, const struct tm *)); 59 60 size_t 61 strftime(s, maxsize, format, t) 62 char *s; 63 size_t maxsize; 64 const char *format; 65 const struct tm *t; 66 { 67 tzset(); 68 69 pt = s; 70 if ((gsize = maxsize) < 1) 71 return (0); 72 if (_fmt(format, t)) { 73 *pt = '\0'; 74 return (maxsize - gsize); 75 } 76 return (0); 77 } 78 79 #define SUN_WEEK(t) (((t)->tm_yday + 7 - \ 80 ((t)->tm_wday)) / 7) 81 #define MON_WEEK(t) (((t)->tm_yday + 7 - \ 82 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7) 83 static size_t 84 _fmt(format, t) 85 register const char *format; 86 const struct tm *t; 87 { 88 for (; *format; ++format) { 89 if (*format == '%') { 90 ++format; 91 if (*format == 'E') { 92 /* Alternate Era */ 93 ++format; 94 } else if (*format == 'O') { 95 /* Alternate numeric symbols */ 96 ++format; 97 } 98 switch (*format) { 99 case '\0': 100 --format; 101 break; 102 case 'A': 103 if (t->tm_wday < 0 || t->tm_wday > 6) 104 return (0); 105 if (!_add(_CurrentTimeLocale->day[t->tm_wday])) 106 return (0); 107 continue; 108 case 'a': 109 if (t->tm_wday < 0 || t->tm_wday > 6) 110 return (0); 111 if (!_add(_CurrentTimeLocale->abday[t->tm_wday])) 112 return (0); 113 continue; 114 case 'B': 115 if (t->tm_mon < 0 || t->tm_mon > 11) 116 return (0); 117 if (!_add(_CurrentTimeLocale->mon[t->tm_mon])) 118 return (0); 119 continue; 120 case 'b': 121 case 'h': 122 if (t->tm_mon < 0 || t->tm_mon > 11) 123 return (0); 124 if (!_add(_CurrentTimeLocale->abmon[t->tm_mon])) 125 return (0); 126 continue; 127 case 'C': 128 if (!_conv((t->tm_year + TM_YEAR_BASE) / 100, 129 2, '0')) 130 return (0); 131 continue; 132 case 'c': 133 if (!_fmt(_CurrentTimeLocale->d_t_fmt, t)) 134 return (0); 135 continue; 136 case 'D': 137 if (!_fmt("%m/%d/%y", t)) 138 return (0); 139 continue; 140 case 'd': 141 if (!_conv(t->tm_mday, 2, '0')) 142 return (0); 143 continue; 144 case 'e': 145 if (!_conv(t->tm_mday, 2, ' ')) 146 return (0); 147 continue; 148 case 'H': 149 if (!_conv(t->tm_hour, 2, '0')) 150 return (0); 151 continue; 152 case 'I': 153 if (!_conv(t->tm_hour % 12 ? 154 t->tm_hour % 12 : 12, 2, '0')) 155 return (0); 156 continue; 157 case 'j': 158 if (!_conv(t->tm_yday + 1, 3, '0')) 159 return (0); 160 continue; 161 case 'k': 162 if (!_conv(t->tm_hour, 2, ' ')) 163 return (0); 164 continue; 165 case 'l': 166 if (!_conv(t->tm_hour % 12 ? 167 t->tm_hour % 12: 12, 2, ' ')) 168 return (0); 169 continue; 170 case 'M': 171 if (!_conv(t->tm_min, 2, '0')) 172 return (0); 173 continue; 174 case 'm': 175 if (!_conv(t->tm_mon + 1, 2, '0')) 176 return (0); 177 continue; 178 case 'n': 179 if (!_add("\n")) 180 return (0); 181 continue; 182 case 'p': 183 if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour >= 12])) 184 return (0); 185 continue; 186 case 'R': 187 if (!_fmt("%H:%M", t)) 188 return (0); 189 continue; 190 case 'r': 191 if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t)) 192 return (0); 193 continue; 194 case 'S': 195 if (!_conv(t->tm_sec, 2, '0')) 196 return (0); 197 continue; 198 case 's': 199 if (!_secs(t)) 200 return (0); 201 continue; 202 case 'T': 203 if (!_fmt("%H:%M:%S", t)) 204 return (0); 205 continue; 206 case 't': 207 if (!_add("\t")) 208 return (0); 209 continue; 210 case 'U': 211 if (!_conv(SUN_WEEK(t), 2, '0')) 212 return (0); 213 continue; 214 case 'u': 215 if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0')) 216 return (0); 217 continue; 218 case 'V': 219 { 220 /* ISO 8601 Week Of Year: 221 * If the week (Monday - Sunday) containing 222 * January 1 has four or more days in the new 223 * year, then it is week 1; otherwise it is 224 * week 53 of the previous year and the next 225 * week is week one. 226 */ 227 228 int week = MON_WEEK(t); 229 230 int days = (((t)->tm_yday + 7 - 231 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) % 7); 232 233 234 if (days >= 4) { 235 week++; 236 } else if (week == 0) { 237 week = 53; 238 } 239 240 if (!_conv(week, 2, '0')) 241 return (0); 242 continue; 243 } 244 case 'W': 245 if (!_conv(MON_WEEK(t), 2, '0')) 246 return (0); 247 continue; 248 case 'w': 249 if (!_conv(t->tm_wday, 1, '0')) 250 return (0); 251 continue; 252 case 'x': 253 if (!_fmt(_CurrentTimeLocale->d_fmt, t)) 254 return (0); 255 continue; 256 case 'X': 257 if (!_fmt(_CurrentTimeLocale->t_fmt, t)) 258 return (0); 259 continue; 260 case 'y': 261 if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, 262 2, '0')) 263 return (0); 264 continue; 265 case 'Y': 266 if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0')) 267 return (0); 268 continue; 269 case 'Z': 270 if (tzname[t->tm_isdst ? 1 : 0] && 271 !_add(tzname[t->tm_isdst ? 1 : 0])) 272 return (0); 273 continue; 274 case '%': 275 /* 276 * X311J/88-090 (4.12.3.5): if conversion char is 277 * undefined, behavior is undefined. Print out the 278 * character itself as printf(3) does. 279 */ 280 default: 281 break; 282 } 283 } 284 if (!gsize--) 285 return (0); 286 *pt++ = *format; 287 } 288 return (gsize); 289 } 290 291 static int 292 _secs(t) 293 const struct tm *t; 294 { 295 static char buf[15]; 296 register time_t s; 297 register char *p; 298 struct tm tmp; 299 300 /* Make a copy, mktime(3) modifies the tm struct. */ 301 tmp = *t; 302 s = mktime(&tmp); 303 for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10) 304 *p-- = s % 10 + '0'; 305 return (_add(++p)); 306 } 307 308 static int 309 _conv(n, digits, pad) 310 int n, digits; 311 char pad; 312 { 313 static char buf[10]; 314 register char *p; 315 316 for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits) 317 *p-- = n % 10 + '0'; 318 while (p > buf && digits-- > 0) 319 *p-- = pad; 320 return (_add(++p)); 321 } 322 323 static int 324 _add(str) 325 register const char *str; 326 { 327 for (;; ++pt, --gsize) { 328 if (!gsize) 329 return (0); 330 if (!(*pt = *str++)) 331 return (1); 332 } 333 } 334