scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

strftime.c (5091B)


      1 #include <time.h>
      2 #include <string.h>
      3 
      4 #include "../libc.h"
      5 #undef strftime
      6 
      7 static char *days[] = {
      8 	"Sunday",   "Monday", "Tuesday",  "Wednesday",
      9 	"Thursday", "Friday", "Saturday",
     10 };
     11 
     12 static char *months[] = {
     13 	"January",   "February", "March",    "April",
     14 	"May",       "June",     "July",     "August",
     15 	"September", "October",  "November", "December"
     16 };
     17 
     18 static char *am_pm[] = {"AM", "PM"};
     19 
     20 static int
     21 first(int day, int year)
     22 {
     23 	int ny;
     24 
     25 	ny = _newyear(year);
     26 	if (ny == day)
     27 		return 0;
     28 	if (day - ny < 0)
     29 		return 7 - (ny - day);
     30 	return day - ny;
     31 }
     32 
     33 
     34 static int
     35 weeknum(struct tm* tm, int day)
     36 {
     37 	int fday, val;
     38 
     39 	fday = first(day, tm->tm_year);
     40 	if (tm->tm_yday < fday) {
     41 		val = 0;
     42 	} else {
     43 		val = tm->tm_yday - fday;
     44 		val /= 7;
     45 		val++;
     46 	}
     47 	return val;
     48 }
     49 
     50 static int
     51 isoyear(struct tm* tm)
     52 {
     53 	int monday;
     54 
     55 	if (tm->tm_yday < 7) {
     56 		monday = first(THU, tm->tm_year) - 3;
     57 		if (tm->tm_yday < monday)
     58 			return tm->tm_year - 1;
     59 	} else if (tm->tm_yday > 357) {
     60 		monday = first(THU, tm->tm_year + 1) - 3;
     61 		if (tm->tm_mday >= (31 + monday))
     62 			return tm->tm_year + 1;
     63 	}
     64 	return tm->tm_year;
     65 }
     66 
     67 static int
     68 isoweek(struct tm* tm)
     69 {
     70 	int year, monday, yday, val;
     71 
     72 	year = isoyear(tm);
     73 	monday = first(THU, year) - 3;
     74 	yday = tm->tm_yday;
     75 	if (year > tm->tm_year) {
     76 		yday = tm->tm_mday - 31 + monday;
     77 	} else if (year < tm->tm_year) {
     78 		yday = _daysyear(year) + yday;
     79 	}
     80 	val = yday - monday;
     81 	val /= 7;
     82 	val++;
     83 	return val;
     84 }
     85 
     86 static size_t
     87 sval(char *s, size_t siz, char **strs, int abrev, int idx, int max)
     88 {
     89 	char *str;
     90 	size_t len;
     91 
     92 	if (idx < 0 && idx >= max)
     93 		goto wrong;
     94 
     95 	str = strs[idx];
     96 	len = (!abrev) ? strlen(str) : 3;
     97 	if (len > siz)
     98 		goto wrong;
     99 
    100 	memcpy(s, str, len);
    101 	return len;
    102 
    103 wrong:
    104 	*s = '?';
    105 	return 1;
    106 }
    107 
    108 static size_t
    109 dval(char *s, size_t siz, int prec, int fill, int val)
    110 {
    111 	char *t;
    112 	int n;
    113 	static char digits[] = "0123456789";
    114 
    115 	if (prec > siz || val < 0) {
    116 		*s = '?';
    117 		return 1;
    118 	}
    119 
    120 	n = prec;
    121 	do {
    122 		s[--n] = digits[val % 10];
    123 		val /= 10;
    124 	} while (n > 0 && val > 0);
    125 
    126 	while (n > 0)
    127 		s[--n] = fill;
    128 
    129 	return prec;
    130 }
    131 
    132 static size_t
    133 timezone(char *s, size_t prec, const struct tm * restrict tm)
    134 {
    135 	long off = tm->tm_gmtoff;
    136 
    137 	if (prec < 5) {
    138 		*s = '?';
    139 		return 1;
    140 	}
    141 
    142 	if (off >= 0) {
    143 		*s++ = '+';
    144 	} else {
    145 		*s++ = '-';
    146 		off = -off;
    147 	}
    148 
    149 	dval(s, 2, 2, '0', off / 3600);
    150 	dval(s, 2, 2, '0', (off % 3600) / 60);
    151 
    152 	return 5;
    153 }
    154 
    155 size_t
    156 strftime(char * restrict s, size_t siz,
    157          const char * restrict fmt,
    158          const struct tm * restrict tm)
    159 {
    160 	int ch, abrev, val, fill, width;
    161 	size_t n, inc;
    162 	char *tfmt;
    163 
    164 	for (n = siz-1; (ch = *fmt++) && n > 0; s += inc, n -= inc) {
    165 		if (ch != '%') {
    166 			*s = ch;
    167 			inc = 1;
    168 			continue;
    169 		}
    170 
    171 		abrev = 0;
    172 		fill = '0';
    173 		width = 2;
    174 
    175 		if (*fmt == 'E' || *fmt == 'O') {
    176 			fmt++;
    177 		}
    178 
    179 		switch (*fmt++) {
    180 		case 'Z':
    181 			if (!tm->tm_zone)
    182 				break;
    183 			inc = sval(s, n, &tm->tm_zone, 0, 0, 1);
    184 			break;
    185 		case 'a':
    186 			abrev = 1;
    187 		case 'A':
    188 			inc = sval(s, n, days, abrev, tm->tm_wday, 7);
    189 			break;
    190 		case 'h':
    191 		case 'b':
    192 			abrev = 1;
    193 		case 'B':
    194 			inc = sval(s, n, months, abrev, tm->tm_mon, 12);
    195 			break;
    196 		case 'p':
    197 			inc = sval(s, n, am_pm, 0, tm->tm_hour > 12, 2);
    198 			break;
    199 		case 'c':
    200 			tfmt = "%a %b %e %T %Y";
    201 			goto recursive;
    202 		case 'D':
    203 			tfmt = "%m/%d/%y";
    204 			goto recursive;
    205 		case 'F':
    206 			tfmt = "%Y-%m-%d";
    207 			goto recursive;
    208 		case 'R':
    209 			tfmt = "%H:%M";
    210 			goto recursive;
    211 		case 'X':
    212 		case 'T':
    213 			tfmt = "%H:%M:%S";
    214 			goto recursive;
    215 		case 'r':
    216 			tfmt = "%I:%M:%S %p";
    217 			goto recursive;
    218 		case 'x':
    219 			tfmt = "%m/%d/%y";
    220 			goto recursive;
    221 		recursive:
    222 			inc = strftime(s, n+1, tfmt, tm) - 1;
    223 			break;
    224 		case 'n':
    225 			val = '\n';
    226 			goto character;
    227 		case 't':
    228 			val = '\t';
    229 			goto character;
    230 		case '%':
    231 			val = '%';
    232 		character:
    233 			*s = val;
    234 			inc = 1;
    235 			break;
    236 		case 'e':
    237 			fill = ' ';
    238 			val = tm->tm_mday;
    239 			goto number;
    240 		case 'd':
    241 			val = tm->tm_mday;
    242 			goto number;
    243 		case 'V':
    244 			val = isoweek(tm);
    245 			goto number;
    246 		case 'g':
    247 			val = isoyear(tm);
    248 			goto number;
    249 		case 'G':
    250 			val = isoyear(tm);
    251 			val += 1900;
    252 			width = 4;
    253 			goto number;
    254 		case 'C':
    255 			val = (tm->tm_year + 1900) / 100;
    256 			goto number;
    257 		case 'H':
    258 			val = tm->tm_hour;
    259 			goto number;
    260 		case 'I':
    261 			val = tm->tm_hour;
    262 			if (val == 0)
    263 				val = 12;
    264 			if (val > 12)
    265 				val -= 12;
    266 			goto number;
    267 		case 'j':
    268 			width = 3;
    269 			val = tm->tm_yday+1;
    270 			goto number;
    271 		case 'm':
    272 			val = tm->tm_mon+1;
    273 			goto number;
    274 		case 'M':
    275 			val = tm->tm_min;
    276 			goto number;
    277 		case 'S':
    278 			val = tm->tm_sec;
    279 			goto number;
    280 		case 'u':
    281 			width = 1;
    282 			val = tm->tm_wday+1;
    283 			goto number;
    284 		case 'U':
    285 			val = weeknum(tm, SUN);
    286 			goto number;
    287 		case 'W':
    288 			val = weeknum(tm, MON);
    289 			goto number;
    290 		case 'w':
    291 			width = 1;
    292 			val = tm->tm_wday;
    293 			goto number;
    294 		case 'y':
    295 			val = tm->tm_year%100;
    296 			goto number;
    297 		case 'Y':
    298 			width = 4;
    299 			val = 1900 + tm->tm_year;
    300 		number:
    301 			inc = dval(s, n, width, fill, val);
    302 			break;
    303 		case 'z':
    304 			inc = timezone(s, n, tm);
    305 			break;
    306 		case '\0':
    307 			inc = 0;
    308 			--fmt;
    309 			break;
    310 		}
    311 	}
    312 	*s = '\0';
    313 
    314 	return siz - n;
    315 }