#include "cutil.h"

#include <ctype.h>

/* 
     strptime() converts the character string pointed to  by  buf
     to a time value, which is stored in the tm structure pointed
     to by tm, using the format specified by fmt.  A  pointer  to
     the  character  following  the  last character in the string
     pointed to by buf is returned.  fmt is  a  character  string
     that consists of field descriptors and text characters, rem-
     iniscent of scanf(3v).  Each field descriptor consists of  a
     %  character followd by another character that specifies the
     replacement for the field descriptor.  All other  characters
     are  copied  from  fmt into the result.  The following field
     descriptors are supported:

          %%   same as %

          %a
          %A   day of week, using locale's weekday names;  either
               the abbreviated or full name may be specified

          %b
          %B
          %h   month, using  locale's  month  names;  either  the
               abbreviated or full name may be specified

          %c   date and time as %x %X

          %C   date and time, in locale's  long-format  date  and
               time representation

          %d
          %e   day of month (1-31; leading zeroes  are  permitted
               but not required)

          %D   date as %m/%d/%y

          %H
          %k   hour (0-23; leading zeroes are permitted  but  not

               required)

          %I
          %l   hour (0-12; leading zeroes are permitted  but  not
               required)

          %j   day number of year (001-366)

          %m   month number (1-12; leading zeroes  are  permitted
               but not required)

          %M   minute (0-59; leading zeroes are permitted but not
               required)

          %p   locale's equivalent of AM or PM

          %r   time as %I:%M:%S %p

          %R   time as %H:%M

          %S   seconds (0-59; leading zeroes  are  permitted  but
               not required)

          %T   time as %H:%M:%S

          %x   date, using locale's date format

          %X   time, using locale's time format

          %y   year within century (0-99; leading zeroes are per-
               mitted but not required)

          %Y   year, including century (for example, 1988)

     Case is ignored when matching items such as month or weekday
     names.   The %M, %S, %y, and %Y fields are optional; if they
     would be matched by white space, the match is suppressed and
     the  appropriate  field of the tm structure pointed to by tm
     is left unchanged.  If any of the format items %d,  %e,  %H,
     %k,  %I,  %l,  %m,  %M,  %S,  %y, or %Y are matched, but the
     string that matches them is followed  by  white  space,  all
     subsequent  items  in  the  format  string are skipped up to
     white space or the end of the format.   The  net  result  is
     that, for example, the format %m/%d/%y can be matched by the
     string 12/31; the tm_mon and tm_mday fields of the tm struc-
     ture  pointed  to  by  tm  will be set to 11 and 31, respec-
     tively, while the tm_year field will be unchanged.

 */

/*
struct	tm {
	int	tm_sec;
	int	tm_min;
	int	tm_hour;
	int	tm_mday;
	int	tm_mon;
	int	tm_year;
	int	tm_wday;
	int	tm_yday;
	int	tm_isdst;
	char	*tm_zone;
	long	tm_gmtoff;
};
*/

#define CHECK_FOR_SPACES(buf, fmt) 			\
    if ((buf == NULL) || isspace(*buf) || (*buf == '\0')) { \
	while ((! isspace(**fmt)) && (**fmt != '\0')) { \
	    (*fmt)++;					\
	}						\
	(*fmt)--;					\
    }							\
  return(buf)

static int substrmatch(char *, char *);
static int substrmatch (str, match)
     char *str;
     char *match;
{
    int match_len = 0;

    while ((*str != '\0') && (*match != '\0')) {
	if (tolower(*str) != tolower(*match)) {
	    return(match_len);
	}
	str++;
	match++;
	match_len++;
    }
    return(match_len);
}  /* substrmatch() */


static char *weekday_names[] = {
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
};  /* weekday_names[] */

static char *month_names[] = {
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
};  /* month_names[] */

static char *get_weekday_name(char *, char **, struct tm *);
static char *get_weekday_name (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int i;
    int len;
    int num = sizeof(weekday_names) / sizeof(weekday_names[0]);

    for (i = 0; i < num; i++) {
	len = substrmatch(buf, weekday_names[i]);
	if (len > 2) {
	    tm->tm_wday = i;
	    return(& buf[len]);
	}
    }
    return(NULL);
}  /* get_weekday_name() */

static char *get_month_name(char *, char **, struct tm *);
static char *get_month_name (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int i;
    int len;
    int num = sizeof(month_names) / sizeof(month_names[0]);

    for (i = 0; i < num; i++) {
	len = substrmatch(buf, month_names[i]);
	if (len > 2) {
	    tm->tm_mon = i;
	    return(& buf[len]);
	}
    }
    return(NULL);
}  /* get_month_name() */


static char *match_number(char *, int *, int, int);
static char *match_number (buf, valp, min, max)
     char *buf;
     int *valp;
     int min;
     int max;
{
    int num = 0;
    int newnum;

    while (isdigit(*buf)) {
	newnum = num * 10 + (*buf - '0');
	if (newnum > max) {
	    *valp = num;
	    return(buf);
	}
	num = newnum;
	buf++;
    }
    *valp = num;
    return(num >= min ? buf : NULL);
}

static char *get_month_num(char *, char **, struct tm *);
static char *get_month_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;
    
    buf = match_number(buf, &val, 1, 12);
    tm->tm_mon = val - 1;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_month_num() */

static char *get_year_num(char *, char **, struct tm *);
static char *get_year_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 0, 2000);
    tm->tm_year = val % 100;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_year_num() */

static char *get_hour_num(char *, char **, struct tm *);
static char *get_hour_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 0, 23);
    tm->tm_hour = val;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_hour_num() */

static char *get_minute_num(char *, char **, struct tm *);
static char *get_minute_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 0, 59);
    tm->tm_min = val;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_minute_num() */

static char *get_second_num(char *, char **, struct tm *);
static char *get_second_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 0, 59);
    tm->tm_sec = val;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_second_num() */

static char *get_day_of_year_num(char *, char **, struct tm *);
static char *get_day_of_year_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 1, 366);
    tm->tm_yday = val - 1;
    return(buf);
}  /* get_day_of_year_num() */

static char *get_day_of_month_num(char *, char **, struct tm *);
static char *get_day_of_month_num (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    int val = 0;

    buf = match_number(buf, &val, 1, 31);
    tm->tm_mday = val;
    CHECK_FOR_SPACES(buf, fmt);
}  /* get_day_of_month_num() */

static char *get_am_or_pm(char *, char **, struct tm *);
static char *get_am_or_pm (buf, fmt, tm)
     char *buf;
     char **fmt;
     struct tm *tm;
{
    if (strncasecmp(buf, "am", 2) == 0) {
	return(& buf[2]);
    }
    else if (strncasecmp(buf, "a.m.", 4) == 0) {
	return(& buf[4]);
    }
    if (strncasecmp(buf, "pm", 2) == 0) {
	buf += 2;
    }
    else if (strncasecmp(buf, "p.m.", 4) == 0) {
	buf += 4;
    }
    else return(NULL);
    if (tm->tm_hour < 12) {
	tm->tm_hour += 12;
    }
    return(buf);
}  /* get_am_or_pm() */

char *string_to_time (buf, fmt, tm)
     char *buf;
     char *fmt;
     struct tm *tm;
{
    for (; *fmt != '\0'; fmt++) {
	if (*fmt == '%') {
	    fmt++;
	    switch (*fmt) {
	      case 'a' :	/* weekday (abbrev or full) */
	      case 'A' :
		buf = get_weekday_name(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		break;

	      case 'b' :	/* month (abbrev or full) */
	      case 'B' :
	      case 'h' :
		buf = get_month_name(buf, &fmt, tm);
		break;

	      case 'c' :	/* same as %x %X */
		break;

	      case 'C' : 	/* locale's long-format date and time */
		break;

	      case 'd' :	/* day of month 1-31, leading 0's allowed */
	      case 'e' :
		buf = get_day_of_month_num(buf, &fmt, tm);
		break;

	      case 'D' :	/* date as %m/%d/%y */
		buf = get_month_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf != '/') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf++;
		buf = get_day_of_month_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != '/') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_year_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		break;

	      case 'H' :	/* hour 0-23, leading 0's allowed */
	      case 'k' :
		buf = get_hour_num(buf, &fmt, tm);
		break;

	      case 'l' :	/* hour 0-12, leading 0's allowed */
	      case 'L' :
		buf = get_hour_num(buf, &fmt, tm);
		break;

	      case 'j' :	/* day number of year 001-366 */
		buf = get_day_of_year_num(buf, &fmt, tm);
		break;

	      case 'm' :	/* month number 1-12, leading 0's allowed */
		buf = get_month_num(buf, &fmt, tm);
		break;

	      case 'M' :	/* minute, 0-59, leading 0's allowed */
		buf = get_minute_num(buf, &fmt, tm);
		break;

	      case 'p' :	/* AM or PM */
		buf = get_am_or_pm(buf, &fmt, tm);
		break;

	      case 'r' :	/* %l:%M:%S %p */
		buf = get_hour_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != ':') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_minute_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != ':') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_second_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		while (isspace(*buf)) {
		    buf++;
		}
		buf = get_am_or_pm(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		break;

	      case 'R' :	/* %H:%M */
		buf = get_hour_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != ':') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_minute_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		break;

	      case 'S' :	/* seconds 0-59, leading 0's allowed */
		buf = get_second_num(buf, &fmt, tm);
		break;

	      case 'T' :	/* time as %H:%M:%S */
		buf = get_hour_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != ':') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_minute_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		if (*buf++ != ':') {
		    if (isspace(*buf)) {
			break;
		    }
		    return(NULL);
		}
		buf = get_second_num(buf, &fmt, tm);
		if (buf == NULL) {
		    return(NULL);
		}
		break;

	      case 'x' :	/* date, using locale's date format */
		break;

	      case 'X' :	/* time, using locale's time format */
		break;

	      case 'y' :	/* year 0-99, leading 0's allowed */
		buf = get_year_num(buf, &fmt, tm);
		break;

	      case 'Y' :	/* year with century ie. 1988 */
		buf = get_year_num(buf, &fmt, tm);
		break;

	      case '%' :  /* matches '%' */
	      default :
		if (*buf != *fmt) {
		    return(NULL);
		}
		buf++;
		break;
	    }  /* switch */
	    if (buf == NULL) {
		return(NULL);
	    }
	}  /* *fmt == '%' */
	else if (isspace(*fmt)) {
	    /* match any number of spaces, even 0 */
	    while (isspace(*buf)) {  /* advance buf */
		buf++;
	    }
	    while (isspace(*fmt)) {  /* advance fmt */
		fmt++;
	    }
	    fmt--;
	}
	else {  /* anything else */
	    if (*buf != *fmt) {
		return(NULL);
	    }
	    buf++;
	}
    }
    return(buf);
}  /* string_to_time() */


main (argc, argv)
     int argc;
     char *argv[];
{
    char input[101];
    struct tm tm;
    char *ptr;
    if(argc != 2){
      printf("usage: format\n");
      exit(-1);
    }
    while (TRUE) {
	bzero((char *) &tm, sizeof(tm));
	printf("\nEnter date --> ");
	gets(input);
	if (input[0] == 'q') exit(0);
	ptr = string_to_time(input, argv[1], &tm);
	if (ptr == NULL) {
	    printf("Returned NULL\n");
	}
	else {
	    printf("Read %d chars, '%s'\n",
		   ptr - input, ptr);
	}
	printf("\tm_sec\t%d\n", tm.tm_sec);
	printf("\tm_min\t%d\n", tm.tm_min);
	printf("\tm_hour\t%d\n", tm.tm_hour);
	printf("\tm_mday\t%d\n", tm.tm_mday);
	printf("\tm_mon\t%d\n", tm.tm_mon);
	printf("\tm_year\t%d\n", tm.tm_year);
	printf("\tm_wday\t%d\n", tm.tm_wday);
	printf("\tm_yday\t%d\n", tm.tm_yday);
    }  /* while () */
}


