* libc/strptime.cc: Implement support for era, alt_digits and POSIX
padding and width modifiers. (era_info_t): New type. (free_era_info): New static function to free era_info_t storage. (get_era_info): New static function to create era_info_t storage from LC_TIME era information. (alt_digits_t): New type. (get_alt_digits): New static function to create alt_digits_t storage from LC_TIME alt_digits information. (free_alt_digits): New static function to free alt_digits_t storage. (find_alt_digits): New static function to scan input for alternative digits and return them, if any. Return NULL otherwise. (__strptime): New static function taking all code from strptime. Implement handling for E, O, +, 0, and width modifiers per POSIX-1.2008. (strptime): Convert into wrapper function to provide era_info and alt_digits pointers and call __strptime. (conv_num): Take additional alt_digits_t parameter and if it's not NULL, call find_alt_digits to convert.
This commit is contained in:
		@@ -41,6 +41,7 @@ __RCSID("$NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $");
 | 
			
		||||
#include <sys/localedef.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <locale.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
@@ -63,24 +64,240 @@ __weak_alias(strptime,_strptime)
 | 
			
		||||
 | 
			
		||||
static const char gmt[4] = { "GMT" };
 | 
			
		||||
 | 
			
		||||
static const u_char *conv_num(const unsigned char *, int *, uint, uint);
 | 
			
		||||
typedef struct _era_info_t {
 | 
			
		||||
  size_t num;		/* Only in first entry: Number of entries,
 | 
			
		||||
			   1 otherwise. */
 | 
			
		||||
  int dir;		/* Direction */
 | 
			
		||||
  long offset;		/* Number of year closest to start_date in the era. */
 | 
			
		||||
  struct tm start;	/* Start date of era */
 | 
			
		||||
  struct tm end;	/* End date of era */
 | 
			
		||||
  CHAR *era_C;		/* Era string */
 | 
			
		||||
  CHAR *era_Y;		/* Replacement for %EY */
 | 
			
		||||
} era_info_t;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
free_era_info (era_info_t *era_info)
 | 
			
		||||
{
 | 
			
		||||
  size_t num = era_info->num;
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < num; ++i)
 | 
			
		||||
    {
 | 
			
		||||
      free (era_info[i].era_C);
 | 
			
		||||
      free (era_info[i].era_Y);
 | 
			
		||||
    }
 | 
			
		||||
  free (era_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static era_info_t *
 | 
			
		||||
get_era_info (const char *era)
 | 
			
		||||
{
 | 
			
		||||
  char *c;
 | 
			
		||||
  era_info_t *ei = NULL;
 | 
			
		||||
  size_t num = 0, cur = 0, len;
 | 
			
		||||
 | 
			
		||||
  while (*era)
 | 
			
		||||
    {
 | 
			
		||||
      ++num;
 | 
			
		||||
      era_info_t *tmp = (era_info_t *) realloc (ei, num * sizeof (era_info_t));
 | 
			
		||||
      if (!tmp)
 | 
			
		||||
	{
 | 
			
		||||
	  ei->num = cur;
 | 
			
		||||
	  free_era_info (ei);
 | 
			
		||||
	  return NULL;
 | 
			
		||||
	}
 | 
			
		||||
      ei = tmp;
 | 
			
		||||
      ei[cur].num = 1;
 | 
			
		||||
      ei[cur].dir = (*era == '+') ? 1 : -1;
 | 
			
		||||
      era += 2;
 | 
			
		||||
      ei[cur].offset = strtol (era, &c, 10);
 | 
			
		||||
      era = c + 1;
 | 
			
		||||
      ei[cur].start.tm_year = strtol (era, &c, 10);
 | 
			
		||||
      /* Adjust offset for negative gregorian dates. */
 | 
			
		||||
      if (ei[cur].start.tm_year < 0)
 | 
			
		||||
	++ei[cur].start.tm_year;
 | 
			
		||||
      ei[cur].start.tm_mon = strtol (c + 1, &c, 10);
 | 
			
		||||
      ei[cur].start.tm_mday = strtol (c + 1, &c, 10);
 | 
			
		||||
      ei[cur].start.tm_hour = ei[cur].start.tm_min = ei[cur].start.tm_sec = 0;
 | 
			
		||||
      era = c + 1;
 | 
			
		||||
      if (era[0] == '-' && era[1] == '*')
 | 
			
		||||
	{
 | 
			
		||||
	  ei[cur].end = ei[cur].start;
 | 
			
		||||
	  ei[cur].start.tm_year = INT_MIN;
 | 
			
		||||
	  ei[cur].start.tm_mon = ei[cur].start.tm_mday = ei[cur].start.tm_hour
 | 
			
		||||
	  = ei[cur].start.tm_min = ei[cur].start.tm_sec = 0;
 | 
			
		||||
	  era += 3;
 | 
			
		||||
	}
 | 
			
		||||
      else if (era[0] == '+' && era[1] == '*')
 | 
			
		||||
	{
 | 
			
		||||
	  ei[cur].end.tm_year = INT_MAX;
 | 
			
		||||
	  ei[cur].end.tm_mon = 12;
 | 
			
		||||
	  ei[cur].end.tm_mday = 31;
 | 
			
		||||
	  ei[cur].end.tm_hour = 23;
 | 
			
		||||
	  ei[cur].end.tm_min = ei[cur].end.tm_sec = 59;
 | 
			
		||||
	  era += 3;
 | 
			
		||||
	}
 | 
			
		||||
      else
 | 
			
		||||
	{
 | 
			
		||||
	  ei[cur].end.tm_year = strtol (era, &c, 10);
 | 
			
		||||
	  /* Adjust offset for negative gregorian dates. */
 | 
			
		||||
	  if (ei[cur].end.tm_year < 0)
 | 
			
		||||
	    ++ei[cur].end.tm_year;
 | 
			
		||||
	  ei[cur].end.tm_mon = strtol (c + 1, &c, 10);
 | 
			
		||||
	  ei[cur].end.tm_mday = strtol (c + 1, &c, 10);
 | 
			
		||||
	  ei[cur].end.tm_mday = 31;
 | 
			
		||||
	  ei[cur].end.tm_hour = 23;
 | 
			
		||||
	  ei[cur].end.tm_min = ei[cur].end.tm_sec = 59;
 | 
			
		||||
	  era = c + 1;
 | 
			
		||||
	}
 | 
			
		||||
      /* era_C */
 | 
			
		||||
      c = strchr (era, ':');
 | 
			
		||||
      len = c - era;
 | 
			
		||||
      ei[cur].era_C = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
 | 
			
		||||
      if (!ei[cur].era_C)
 | 
			
		||||
	{
 | 
			
		||||
	  ei->num = cur;
 | 
			
		||||
	  free_era_info (ei);
 | 
			
		||||
	  return NULL;
 | 
			
		||||
	}
 | 
			
		||||
      strncpy (ei[cur].era_C, era, len);
 | 
			
		||||
      era += len;
 | 
			
		||||
      ei[cur].era_C[len] = '\0';
 | 
			
		||||
      /* era_Y */
 | 
			
		||||
      ++era;
 | 
			
		||||
      c = strchr (era, ';');
 | 
			
		||||
      if (!c)
 | 
			
		||||
	c = strchr (era, '\0');
 | 
			
		||||
      len = c - era;
 | 
			
		||||
      ei[cur].era_Y = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
 | 
			
		||||
      if (!ei[cur].era_Y)
 | 
			
		||||
	{
 | 
			
		||||
	  free (ei[cur].era_C);
 | 
			
		||||
	  ei->num = cur;
 | 
			
		||||
	  free_era_info (ei);
 | 
			
		||||
	  return NULL;
 | 
			
		||||
	}
 | 
			
		||||
      strncpy (ei[cur].era_Y, era, len);
 | 
			
		||||
      era += len;
 | 
			
		||||
      ei[cur].era_Y[len] = '\0';
 | 
			
		||||
      ++cur;
 | 
			
		||||
      if (*c)
 | 
			
		||||
	era = c + 1;
 | 
			
		||||
    }
 | 
			
		||||
  ei->num = num;
 | 
			
		||||
  return ei;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct _alt_digits_t {
 | 
			
		||||
  size_t num;
 | 
			
		||||
  char **digit;
 | 
			
		||||
  char *buffer;
 | 
			
		||||
} alt_digits_t;
 | 
			
		||||
 | 
			
		||||
static alt_digits_t *
 | 
			
		||||
get_alt_digits (const char *alt_digits)
 | 
			
		||||
{
 | 
			
		||||
  alt_digits_t *adi;
 | 
			
		||||
  const char *a, *e;
 | 
			
		||||
  char *aa, *ae;
 | 
			
		||||
  size_t len;
 | 
			
		||||
 | 
			
		||||
  adi = (alt_digits_t *) calloc (1, sizeof (alt_digits_t));
 | 
			
		||||
  if (!adi)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  /* Compute number of alt_digits. */
 | 
			
		||||
  adi->num = 1;
 | 
			
		||||
  for (a = alt_digits; (e = strchr (a, ';')) != NULL; a = e + 1)
 | 
			
		||||
      ++adi->num;
 | 
			
		||||
  /* Allocate the `digit' array, which is an array of `num' pointers into
 | 
			
		||||
     `buffer'. */
 | 
			
		||||
  adi->digit = (CHAR **) calloc (adi->num, sizeof (CHAR **));
 | 
			
		||||
  if (!adi->digit)
 | 
			
		||||
    {
 | 
			
		||||
      free (adi);
 | 
			
		||||
      return NULL;
 | 
			
		||||
    }
 | 
			
		||||
  /* Compute memory required for `buffer'. */
 | 
			
		||||
  len = strlen (alt_digits);
 | 
			
		||||
  /* Allocate it. */
 | 
			
		||||
  adi->buffer = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
 | 
			
		||||
  if (!adi->buffer)
 | 
			
		||||
    {
 | 
			
		||||
      free (adi->digit);
 | 
			
		||||
      free (adi);
 | 
			
		||||
      return NULL;
 | 
			
		||||
    }
 | 
			
		||||
  /* Store digits in it. */
 | 
			
		||||
  strcpy (adi->buffer, alt_digits);
 | 
			
		||||
  /* Store the pointers into `buffer' into the appropriate `digit' slot. */
 | 
			
		||||
  for (len = 0, aa = adi->buffer; (ae = strchr (aa, ';')) != NULL;
 | 
			
		||||
       ++len, aa = ae + 1)
 | 
			
		||||
    {
 | 
			
		||||
      *ae = '\0';
 | 
			
		||||
      adi->digit[len] = aa;
 | 
			
		||||
    }
 | 
			
		||||
  adi->digit[len] = aa;
 | 
			
		||||
  return adi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
free_alt_digits (alt_digits_t *adi)
 | 
			
		||||
{
 | 
			
		||||
  free (adi->digit);
 | 
			
		||||
  free (adi->buffer);
 | 
			
		||||
  free (adi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const unsigned char *
 | 
			
		||||
find_alt_digits (const unsigned char *bp, alt_digits_t *adi, uint *pval)
 | 
			
		||||
{
 | 
			
		||||
  /* This is rather error-prone, but the entire idea of alt_digits
 | 
			
		||||
     isn't thought out well.  If you start to look for matches at the
 | 
			
		||||
     start, there's a high probability that you find short matches but
 | 
			
		||||
     the entire translation is wrong.  So we scan the alt_digits array
 | 
			
		||||
     from the highest to the lowest digits instead, hoping that it's
 | 
			
		||||
     more likely to catch digits consisting of multiple characters. */
 | 
			
		||||
  for (int i = (int) adi->num - 1; i >= 0; --i)
 | 
			
		||||
    {
 | 
			
		||||
      size_t len = strlen (adi->digit[i]);
 | 
			
		||||
      if (!strncmp ((const char *) bp, adi->digit[i], len))
 | 
			
		||||
	{
 | 
			
		||||
	  *pval = i;
 | 
			
		||||
	  return bp + len;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This simplifies the calls to conv_num enormously. */
 | 
			
		||||
#define ALT_DIGITS	((alt_format & ALT_O) ? *alt_digits : NULL)
 | 
			
		||||
 | 
			
		||||
static const u_char *conv_num(const unsigned char *, int *, uint, uint,
 | 
			
		||||
			      alt_digits_t *);
 | 
			
		||||
static const u_char *find_string(const u_char *, int *, const char * const *,
 | 
			
		||||
	const char * const *, int);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
strptime(const char *buf, const char *fmt, struct tm *tm)
 | 
			
		||||
static char *
 | 
			
		||||
__strptime(const char *buf, const char *fmt, struct tm *tm,
 | 
			
		||||
	   era_info_t **era_info, alt_digits_t **alt_digits)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char c;
 | 
			
		||||
	const unsigned char *bp;
 | 
			
		||||
	int alt_format, i, split_year = 0;
 | 
			
		||||
	era_info_t *era = NULL;
 | 
			
		||||
	int era_offset, got_eoff = 0;
 | 
			
		||||
	int saw_padding;
 | 
			
		||||
	unsigned long width;
 | 
			
		||||
	const char *new_fmt;
 | 
			
		||||
	uint ulim;
 | 
			
		||||
 | 
			
		||||
	bp = (const u_char *)buf;
 | 
			
		||||
	struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale ();
 | 
			
		||||
 | 
			
		||||
	while (bp != NULL && (c = *fmt++) != '\0') {
 | 
			
		||||
		/* Clear `alternate' modifier prior to new conversion. */
 | 
			
		||||
		saw_padding = 0;
 | 
			
		||||
		width = 0;
 | 
			
		||||
		alt_format = 0;
 | 
			
		||||
		i = 0;
 | 
			
		||||
 | 
			
		||||
@@ -110,18 +327,43 @@ literal:
 | 
			
		||||
		case 'E':	/* "%E?" alternative conversion modifier. */
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			alt_format |= ALT_E;
 | 
			
		||||
			if (!*era_info && *_CurrentTimeLocale->era)
 | 
			
		||||
			  *era_info = get_era_info (_CurrentTimeLocale->era);
 | 
			
		||||
			goto again;
 | 
			
		||||
 | 
			
		||||
		case 'O':	/* "%O?" alternative conversion modifier. */
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			alt_format |= ALT_O;
 | 
			
		||||
			if (!*alt_digits && *_CurrentTimeLocale->alt_digits)
 | 
			
		||||
			  *alt_digits =
 | 
			
		||||
			      get_alt_digits (_CurrentTimeLocale->alt_digits);
 | 
			
		||||
			goto again;
 | 
			
		||||
 | 
			
		||||
		case '0':
 | 
			
		||||
		case '+':
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			if (saw_padding)
 | 
			
		||||
			  return NULL;
 | 
			
		||||
			saw_padding = 1;
 | 
			
		||||
			goto again;
 | 
			
		||||
		case '1': case '2': case '3': case '4': case '5':
 | 
			
		||||
		case '6': case '7': case '8': case '9':
 | 
			
		||||
			/* POSIX-1.2008 maximum field width.  Per POSIX,
 | 
			
		||||
			   the width is only defined for the 'C', 'F', and 'Y'
 | 
			
		||||
			   conversion specifiers. */
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			{
 | 
			
		||||
			  char *end;
 | 
			
		||||
			  width = strtoul (fmt - 1, &end, 10);
 | 
			
		||||
			  fmt = (const char *) end;
 | 
			
		||||
			  goto again;
 | 
			
		||||
			}
 | 
			
		||||
		/*
 | 
			
		||||
		 * "Complex" conversion rules, implemented through recursion.
 | 
			
		||||
		 */
 | 
			
		||||
		case 'c':	/* Date and time, using the locale's format. */
 | 
			
		||||
			new_fmt = _ctloc(c_fmt);
 | 
			
		||||
			new_fmt = (alt_format & ALT_E)
 | 
			
		||||
				  ? _ctloc (era_d_t_fmt) : _ctloc(c_fmt);
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
			goto recurse;
 | 
			
		||||
 | 
			
		||||
		case 'D':	/* The date as "%m/%d/%y". */
 | 
			
		||||
@@ -130,9 +372,15 @@ literal:
 | 
			
		||||
			goto recurse;
 | 
			
		||||
 | 
			
		||||
		case 'F':	/* The date as "%Y-%m-%d". */
 | 
			
		||||
			new_fmt = "%Y-%m-%d";
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			goto recurse;
 | 
			
		||||
			{
 | 
			
		||||
			  LEGAL_ALT(0);
 | 
			
		||||
			  char *tmp = __strptime ((const char *) bp, "%Y-%m-%d",
 | 
			
		||||
						  tm, era_info, alt_digits);
 | 
			
		||||
			  if (tmp && (uint) (tmp - (char *) bp) > width)
 | 
			
		||||
			    return NULL;
 | 
			
		||||
			  bp = (const unsigned char *) tmp;
 | 
			
		||||
			  continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case 'R':	/* The time as "%H:%M". */
 | 
			
		||||
			new_fmt = "%H:%M";
 | 
			
		||||
@@ -150,15 +398,19 @@ literal:
 | 
			
		||||
			goto recurse;
 | 
			
		||||
 | 
			
		||||
		case 'X':	/* The time, using the locale's format. */
 | 
			
		||||
			new_fmt =_ctloc(X_fmt);
 | 
			
		||||
			new_fmt = (alt_format & ALT_E)
 | 
			
		||||
				  ? _ctloc (era_t_fmt) : _ctloc(X_fmt);
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
			goto recurse;
 | 
			
		||||
 | 
			
		||||
		case 'x':	/* The date, using the locale's format. */
 | 
			
		||||
			new_fmt =_ctloc(x_fmt);
 | 
			
		||||
		    recurse:
 | 
			
		||||
			bp = (const u_char *)strptime((const char *)bp,
 | 
			
		||||
							    new_fmt, tm);
 | 
			
		||||
			new_fmt = (alt_format & ALT_E)
 | 
			
		||||
				  ? _ctloc (era_d_fmt) : _ctloc(x_fmt);
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
		    recurse:
 | 
			
		||||
			bp = (const u_char *)__strptime((const char *)bp,
 | 
			
		||||
							new_fmt, tm,
 | 
			
		||||
							era_info, alt_digits);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
@@ -180,58 +432,83 @@ literal:
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'C':	/* The century number. */
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
			if ((alt_format & ALT_E) && *era_info)
 | 
			
		||||
			  {
 | 
			
		||||
			    /* With E modifier, an era.  We potentially
 | 
			
		||||
			       don't know the era offset yet, so we have to
 | 
			
		||||
			       store the value in a local variable.
 | 
			
		||||
			       The final computation of tm_year is only done
 | 
			
		||||
			       right before this function returns. */
 | 
			
		||||
			    size_t num = (*era_info)->num;
 | 
			
		||||
			    for (size_t i = 0; i < num; ++i)
 | 
			
		||||
			      if (!strncmp ((const char *) bp,
 | 
			
		||||
					    (*era_info)[i].era_C,
 | 
			
		||||
					    strlen ((*era_info)[i].era_C)))
 | 
			
		||||
				{
 | 
			
		||||
				  era = (*era_info) + i;
 | 
			
		||||
				  bp += strlen (era->era_C);
 | 
			
		||||
				  break;
 | 
			
		||||
				}
 | 
			
		||||
			    if (!era)
 | 
			
		||||
			      return NULL;
 | 
			
		||||
			    continue;
 | 
			
		||||
			  }
 | 
			
		||||
			i = 20;
 | 
			
		||||
			bp = conv_num(bp, &i, 0, 99);
 | 
			
		||||
			for (ulim = 99; width && width < 2; ++width)
 | 
			
		||||
			  ulim /= 10;
 | 
			
		||||
			bp = conv_num(bp, &i, 0, ulim, NULL);
 | 
			
		||||
 | 
			
		||||
			i = i * 100 - TM_YEAR_BASE;
 | 
			
		||||
			if (split_year)
 | 
			
		||||
				i += tm->tm_year % 100;
 | 
			
		||||
			split_year = 1;
 | 
			
		||||
			tm->tm_year = i;
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
			era = NULL;
 | 
			
		||||
			got_eoff = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'd':	/* The day of month. */
 | 
			
		||||
		case 'e':
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_mday, 1, 31);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_mday, 1, 31, ALT_DIGITS);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'k':	/* The hour (24-hour clock representation). */
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			/* FALLTHROUGH */
 | 
			
		||||
		case 'H':
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_hour, 0, 23);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_hour, 0, 23, ALT_DIGITS);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'l':	/* The hour (12-hour clock representation). */
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			/* FALLTHROUGH */
 | 
			
		||||
		case 'I':
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_hour, 1, 12);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_hour, 1, 12, ALT_DIGITS);
 | 
			
		||||
			if (tm->tm_hour == 12)
 | 
			
		||||
				tm->tm_hour = 0;
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'j':	/* The day of year. */
 | 
			
		||||
			i = 1;
 | 
			
		||||
			bp = conv_num(bp, &i, 1, 366);
 | 
			
		||||
			bp = conv_num(bp, &i, 1, 366, NULL);
 | 
			
		||||
			tm->tm_yday = i - 1;
 | 
			
		||||
			LEGAL_ALT(0);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'M':	/* The minute. */
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_min, 0, 59);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_min, 0, 59, ALT_DIGITS);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'm':	/* The month. */
 | 
			
		||||
			i = 1;
 | 
			
		||||
			bp = conv_num(bp, &i, 1, 12);
 | 
			
		||||
			tm->tm_mon = i - 1;
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			i = 1;
 | 
			
		||||
			bp = conv_num(bp, &i, 1, 12, ALT_DIGITS);
 | 
			
		||||
			tm->tm_mon = i - 1;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'p':	/* The locale's equivalent of AM/PM. */
 | 
			
		||||
@@ -243,8 +520,8 @@ literal:
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'S':	/* The seconds. */
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_sec, 0, 61);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_sec, 0, 61, ALT_DIGITS);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'U':	/* The week of year, beginning on sunday. */
 | 
			
		||||
@@ -255,28 +532,67 @@ literal:
 | 
			
		||||
			 * point to calculate a real value, so just check the
 | 
			
		||||
			 * range for now.
 | 
			
		||||
			 */
 | 
			
		||||
			 bp = conv_num(bp, &i, 0, 53);
 | 
			
		||||
			 LEGAL_ALT(ALT_O);
 | 
			
		||||
			 bp = conv_num(bp, &i, 0, 53, ALT_DIGITS);
 | 
			
		||||
			 continue;
 | 
			
		||||
 | 
			
		||||
		case 'w':	/* The day of week, beginning on sunday. */
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_wday, 0, 6);
 | 
			
		||||
			LEGAL_ALT(ALT_O);
 | 
			
		||||
			bp = conv_num(bp, &tm->tm_wday, 0, 6, ALT_DIGITS);
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'Y':	/* The year. */
 | 
			
		||||
			i = TM_YEAR_BASE;	/* just for data sanity... */
 | 
			
		||||
			bp = conv_num(bp, &i, 0, 9999);
 | 
			
		||||
			tm->tm_year = i - TM_YEAR_BASE;
 | 
			
		||||
			LEGAL_ALT(ALT_E);
 | 
			
		||||
			if ((alt_format & ALT_E) && *era_info)
 | 
			
		||||
			  {
 | 
			
		||||
			    bool gotit = false;
 | 
			
		||||
			    size_t num = (*era_info)->num;
 | 
			
		||||
			    (*era_info)->num = 1;
 | 
			
		||||
			    for (size_t i = 0; i < num; ++i)
 | 
			
		||||
			      {
 | 
			
		||||
				era_info_t *tmp_ei = (*era_info) + i;
 | 
			
		||||
				char *tmp = __strptime ((const char *) bp,
 | 
			
		||||
							tmp_ei->era_Y,
 | 
			
		||||
							tm, &tmp_ei,
 | 
			
		||||
							alt_digits);
 | 
			
		||||
				if (tmp)
 | 
			
		||||
				  {
 | 
			
		||||
				    bp = (const unsigned char *) tmp;
 | 
			
		||||
				    gotit = true;
 | 
			
		||||
				    break;
 | 
			
		||||
				  }
 | 
			
		||||
			      }
 | 
			
		||||
			    (*era_info)->num = num;
 | 
			
		||||
			    if (gotit)
 | 
			
		||||
			      continue;
 | 
			
		||||
			    return NULL;
 | 
			
		||||
			  }
 | 
			
		||||
			i = TM_YEAR_BASE;	/* just for data sanity... */
 | 
			
		||||
			for (ulim = 9999; width && width < 4; ++width)
 | 
			
		||||
			  ulim /= 10;
 | 
			
		||||
			bp = conv_num(bp, &i, 0, ulim, NULL);
 | 
			
		||||
			tm->tm_year = i - TM_YEAR_BASE;
 | 
			
		||||
			era = NULL;
 | 
			
		||||
			got_eoff = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'y':	/* The year within 100 years of the epoch. */
 | 
			
		||||
			/* LEGAL_ALT(ALT_E | ALT_O); */
 | 
			
		||||
			bp = conv_num(bp, &i, 0, 99);
 | 
			
		||||
			if ((alt_format & ALT_E) && *era_info)
 | 
			
		||||
			  {
 | 
			
		||||
			    /* With E modifier, the offset to the start date
 | 
			
		||||
			       of the era specified with %EC.  We potentially
 | 
			
		||||
			       don't know the era yet, so we have to store the
 | 
			
		||||
			       value in a local variable, just like era itself.
 | 
			
		||||
			       The final computation of tm_year is only done
 | 
			
		||||
			       right before this function returns. */
 | 
			
		||||
			    bp = conv_num(bp, &era_offset, 0, UINT_MAX, NULL);
 | 
			
		||||
			    got_eoff = 1;
 | 
			
		||||
			    continue;
 | 
			
		||||
			  }
 | 
			
		||||
			bp = conv_num(bp, &i, 0, 99, ALT_DIGITS);
 | 
			
		||||
 | 
			
		||||
			if (split_year)
 | 
			
		||||
				/* preserve century */
 | 
			
		||||
			if (split_year) /* preserve century */
 | 
			
		||||
				i += (tm->tm_year / 100) * 100;
 | 
			
		||||
			else {
 | 
			
		||||
				split_year = 1;
 | 
			
		||||
@@ -286,6 +602,8 @@ literal:
 | 
			
		||||
					i = i + 1900 - TM_YEAR_BASE;
 | 
			
		||||
			}
 | 
			
		||||
			tm->tm_year = i;
 | 
			
		||||
			era = NULL;
 | 
			
		||||
			got_eoff = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		case 'Z':
 | 
			
		||||
@@ -334,29 +652,66 @@ literal:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bp && (era || got_eoff))
 | 
			
		||||
	  {
 | 
			
		||||
	    /* Default to current era. */
 | 
			
		||||
	    if (!era)
 | 
			
		||||
	      era = *era_info;
 | 
			
		||||
	    /* Default to first year of era if offset is missing */
 | 
			
		||||
	    if (!got_eoff)
 | 
			
		||||
	      era_offset = era->offset;
 | 
			
		||||
	    tm->tm_year = (era->start.tm_year != INT_MIN
 | 
			
		||||
			   ? era->start.tm_year : era->end.tm_year)
 | 
			
		||||
			   + (era_offset - era->offset) * era->dir;
 | 
			
		||||
	    /* Check if year falls into the era.  If not, it's an
 | 
			
		||||
	       invalid combination of era and offset. */
 | 
			
		||||
	    if (era->start.tm_year > tm->tm_year
 | 
			
		||||
	    	|| era->end.tm_year < tm->tm_year)
 | 
			
		||||
	      return NULL;
 | 
			
		||||
	    tm->tm_year -= TM_YEAR_BASE;
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	return (char *) bp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
strptime (const char *buf, const char *fmt, struct tm *tm)
 | 
			
		||||
{
 | 
			
		||||
  era_info_t *era_info = NULL;
 | 
			
		||||
  alt_digits_t *alt_digits = NULL;
 | 
			
		||||
  char *ret = __strptime (buf, fmt, tm, &era_info, &alt_digits);
 | 
			
		||||
  if (era_info)
 | 
			
		||||
    free_era_info (era_info);
 | 
			
		||||
  if (alt_digits)
 | 
			
		||||
    free_alt_digits (alt_digits);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const u_char *
 | 
			
		||||
conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
 | 
			
		||||
conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim,
 | 
			
		||||
	 alt_digits_t *alt_digits)
 | 
			
		||||
{
 | 
			
		||||
	uint result = 0;
 | 
			
		||||
	unsigned char ch;
 | 
			
		||||
 | 
			
		||||
	/* The limit also determines the number of valid digits. */
 | 
			
		||||
	uint rulim = ulim;
 | 
			
		||||
	if (alt_digits)
 | 
			
		||||
	  buf = find_alt_digits (buf, alt_digits, &result);
 | 
			
		||||
	else
 | 
			
		||||
	  {
 | 
			
		||||
	    /* The limit also determines the number of valid digits. */
 | 
			
		||||
	    uint rulim = ulim;
 | 
			
		||||
 | 
			
		||||
	ch = *buf;
 | 
			
		||||
	if (ch < '0' || ch > '9')
 | 
			
		||||
		return NULL;
 | 
			
		||||
	    ch = *buf;
 | 
			
		||||
	    if (ch < '0' || ch > '9')
 | 
			
		||||
		    return NULL;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		result *= 10;
 | 
			
		||||
		result += ch - '0';
 | 
			
		||||
		rulim /= 10;
 | 
			
		||||
		ch = *++buf;
 | 
			
		||||
	} while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
 | 
			
		||||
	    do {
 | 
			
		||||
		    result *= 10;
 | 
			
		||||
		    result += ch - '0';
 | 
			
		||||
		    rulim /= 10;
 | 
			
		||||
		    ch = *++buf;
 | 
			
		||||
	    } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	if (result < llim || result > ulim)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user