strptime.c raw

   1  #include <stdlib.h>
   2  #include <langinfo.h>
   3  #include <time.h>
   4  #include <ctype.h>
   5  #include <stddef.h>
   6  #include <string.h>
   7  #include <strings.h>
   8  
   9  char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
  10  {
  11  	int i, w, neg, adj, min, range, *dest, dummy;
  12  	const char *ex;
  13  	size_t len;
  14  	int want_century = 0, century = 0, relyear = 0;
  15  	while (*f) {
  16  		if (*f != '%') {
  17  			if (isspace(*f)) for (; *s && isspace(*s); s++);
  18  			else if (*s != *f) return 0;
  19  			else s++;
  20  			f++;
  21  			continue;
  22  		}
  23  		f++;
  24  		if (*f == '+') f++;
  25  		if (isdigit(*f)) {
  26  			char *new_f;
  27  			w=strtoul(f, &new_f, 10);
  28  			f = new_f;
  29  		} else {
  30  			w=-1;
  31  		}
  32  		adj=0;
  33  		switch (*f++) {
  34  		case 'a': case 'A':
  35  			dest = &tm->tm_wday;
  36  			min = ABDAY_1;
  37  			range = 7;
  38  			goto symbolic_range;
  39  		case 'b': case 'B': case 'h':
  40  			dest = &tm->tm_mon;
  41  			min = ABMON_1;
  42  			range = 12;
  43  			goto symbolic_range;
  44  		case 'c':
  45  			s = strptime(s, nl_langinfo(D_T_FMT), tm);
  46  			if (!s) return 0;
  47  			break;
  48  		case 'C':
  49  			dest = &century;
  50  			if (w<0) w=2;
  51  			want_century |= 2;
  52  			goto numeric_digits;
  53  		case 'd': case 'e':
  54  			dest = &tm->tm_mday;
  55  			min = 1;
  56  			range = 31;
  57  			goto numeric_range;
  58  		case 'D':
  59  			s = strptime(s, "%m/%d/%y", tm);
  60  			if (!s) return 0;
  61  			break;
  62  		case 'H':
  63  			dest = &tm->tm_hour;
  64  			min = 0;
  65  			range = 24;
  66  			goto numeric_range;
  67  		case 'I':
  68  			dest = &tm->tm_hour;
  69  			min = 1;
  70  			range = 12;
  71  			goto numeric_range;
  72  		case 'j':
  73  			dest = &tm->tm_yday;
  74  			min = 1;
  75  			range = 366;
  76  			adj = 1;
  77  			goto numeric_range;
  78  		case 'm':
  79  			dest = &tm->tm_mon;
  80  			min = 1;
  81  			range = 12;
  82  			adj = 1;
  83  			goto numeric_range;
  84  		case 'M':
  85  			dest = &tm->tm_min;
  86  			min = 0;
  87  			range = 60;
  88  			goto numeric_range;
  89  		case 'n': case 't':
  90  			for (; *s && isspace(*s); s++);
  91  			break;
  92  		case 'p':
  93  			ex = nl_langinfo(AM_STR);
  94  			len = strlen(ex);
  95  			if (!strncasecmp(s, ex, len)) {
  96  				tm->tm_hour %= 12;
  97  				s += len;
  98  				break;
  99  			}
 100  			ex = nl_langinfo(PM_STR);
 101  			len = strlen(ex);
 102  			if (!strncasecmp(s, ex, len)) {
 103  				tm->tm_hour %= 12;
 104  				tm->tm_hour += 12;
 105  				s += len;
 106  				break;
 107  			}
 108  			return 0;
 109  		case 'r':
 110  			s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
 111  			if (!s) return 0;
 112  			break;
 113  		case 'R':
 114  			s = strptime(s, "%H:%M", tm);
 115  			if (!s) return 0;
 116  			break;
 117  		case 'S':
 118  			dest = &tm->tm_sec;
 119  			min = 0;
 120  			range = 61;
 121  			goto numeric_range;
 122  		case 'T':
 123  			s = strptime(s, "%H:%M:%S", tm);
 124  			if (!s) return 0;
 125  			break;
 126  		case 'U':
 127  		case 'W':
 128  			/* Throw away result, for now. (FIXME?) */
 129  			dest = &dummy;
 130  			min = 0;
 131  			range = 54;
 132  			goto numeric_range;
 133  		case 'w':
 134  			dest = &tm->tm_wday;
 135  			min = 0;
 136  			range = 7;
 137  			goto numeric_range;
 138  		case 'x':
 139  			s = strptime(s, nl_langinfo(D_FMT), tm);
 140  			if (!s) return 0;
 141  			break;
 142  		case 'X':
 143  			s = strptime(s, nl_langinfo(T_FMT), tm);
 144  			if (!s) return 0;
 145  			break;
 146  		case 'y':
 147  			dest = &relyear;
 148  			w = 2;
 149  			want_century |= 1;
 150  			goto numeric_digits;
 151  		case 'Y':
 152  			dest = &tm->tm_year;
 153  			if (w<0) w=4;
 154  			adj = 1900;
 155  			want_century = 0;
 156  			goto numeric_digits;
 157  		case '%':
 158  			if (*s++ != '%') return 0;
 159  			break;
 160  		default:
 161  			return 0;
 162  		numeric_range:
 163  			if (!isdigit(*s)) return 0;
 164  			*dest = 0;
 165  			for (i=1; i<=min+range && isdigit(*s); i*=10)
 166  				*dest = *dest * 10 + *s++ - '0';
 167  			if (*dest - min >= (unsigned)range) return 0;
 168  			*dest -= adj;
 169  			switch((char *)dest - (char *)tm) {
 170  			case offsetof(struct tm, tm_yday):
 171  				;
 172  			}
 173  			goto update;
 174  		numeric_digits:
 175  			neg = 0;
 176  			if (*s == '+') s++;
 177  			else if (*s == '-') neg=1, s++;
 178  			if (!isdigit(*s)) return 0;
 179  			for (*dest=i=0; i<w && isdigit(*s); i++)
 180  				*dest = *dest * 10 + *s++ - '0';
 181  			if (neg) *dest = -*dest;
 182  			*dest -= adj;
 183  			goto update;
 184  		symbolic_range:
 185  			for (i=2*range-1; i>=0; i--) {
 186  				ex = nl_langinfo(min+i);
 187  				len = strlen(ex);
 188  				if (strncasecmp(s, ex, len)) continue;
 189  				s += len;
 190  				*dest = i % range;
 191  				break;
 192  			}
 193  			if (i<0) return 0;
 194  			goto update;
 195  		update:
 196  			//FIXME
 197  			;
 198  		}
 199  	}
 200  	if (want_century) {
 201  		tm->tm_year = relyear;
 202  		if (want_century & 2) tm->tm_year += century * 100 - 1900;
 203  		else if (tm->tm_year <= 68) tm->tm_year += 100;
 204  	}
 205  	return (char *)s;
 206  }
 207