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