about summary refs log tree commit diff
path: root/time/strptime.c
diff options
context:
space:
mode:
Diffstat (limited to 'time/strptime.c')
-rw-r--r--time/strptime.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/time/strptime.c b/time/strptime.c
new file mode 100644
index 0000000000..cb3d126b9c
--- /dev/null
+++ b/time/strptime.c
@@ -0,0 +1,344 @@
+/* strptime - Convert a string representation of time to a time value.
+Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <ctype.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+
+#include "../locale/localeinfo.h"
+
+
+#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
+#define match_string(cs1, s2)						      \
+  ({ size_t len = strlen (cs1);						      \
+     int result = strncasecmp (cs1, s2, len) == 0;			      \
+     if (result) s2 += len;						      \
+     result; })
+/* We intentionally do not use isdigit() for testing because this will
+   lead to problems with the wide character version.  */
+#define get_number(from, to)						      \
+  do {									      \
+    val = 0;								      \
+    if (*rp < '0' || *rp > '9')						      \
+      return NULL;							      \
+    do {								      \
+      val *= 10;							      \
+      val += *rp++ - '0';						      \
+    } while (val * 10 <= to && *rp >= '0' && *rp <= '9');		      \
+    if (val < from || val > to)						      \
+      return NULL;							      \
+  } while (0)
+#define get_alt_number(from, to)					      \
+  do {									      \
+    const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);		      \
+    val = 0;								      \
+    while (*alts != '\0')						      \
+      {									      \
+	size_t len = strlen (alts);					      \
+	if (strncasecmp (alts, rp, len) == 0)				      \
+	  break;							      \
+	alts = strchr (alts, '\0') + 1;					      \
+	++val;								      \
+      }									      \
+    if (*alts == '\0')							      \
+      return NULL;							      \
+  } while (0)
+#define recursive(new_fmt)						      \
+  do {									      \
+    if (*new_fmt == '\0')						      \
+      return NULL;							      \
+    rp = strptime (rp, new_fmt, tm);					      \
+    if (rp == NULL)							      \
+      return NULL;							      \
+  } while (0)
+  
+
+char *
+strptime (const char *buf, const char *format, struct tm *tm)
+{
+  const char *rp;
+  const char *fmt;
+  int cnt;
+  size_t val;
+  int have_I, is_pm;
+
+  rp = buf;
+  fmt = format;
+  have_I = is_pm = 0;
+
+  while (*fmt != '\0')
+    {
+      /* A white space in the format string matches 0 more or white
+	 space in the input string.  */
+      if (isspace (*fmt))
+	{
+	  while (isspace (*rp))
+	    ++rp;
+	  ++fmt;
+	  continue;
+	}
+
+      /* Any character but `%' must be matched by the same character
+	 in the iput string.  */
+      if (*fmt != '%')
+	{
+	  match_char (*fmt++, *rp++);
+	  continue;
+	}
+
+      ++fmt;
+      switch (*fmt++)
+	{
+	case '%':
+	  /* Match the `%' character itself.  */
+	  match_char ('%', *rp++);
+	  break;
+	case 'a':
+	case 'A':
+	  /* Match day of week.  */
+	  for (cnt = 0; cnt < 7; ++cnt)
+	    {
+	      if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
+		break;
+	      if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
+		break;
+	    }
+	  if (cnt == 7)
+	    /* Does not match a weekday name.  */
+	    return NULL;
+	  tm->tm_wday = cnt;
+	  break;
+	case 'b':
+	case 'B':
+	case 'h':
+	  /* Match month name.  */
+	  for (cnt = 0; cnt < 12; ++cnt)
+	    {
+	      if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
+		break;
+	      if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
+		break;
+	    }
+	  if (cnt == 12)
+	    /* Does not match a month name.  */
+	    return NULL;
+	  tm->tm_mon = cnt;
+	  break;
+	case 'c':
+	  /* Match locale's date and time format.  */
+	  recursive (_NL_CURRENT (LC_TIME, D_T_FMT));
+	  break;
+	case 'C':
+	  /* Match century number.  */
+	  get_number (0, 99);
+	  /* We don't need the number.  */
+	  break;
+	case 'd':
+	case 'e':
+	  /* Match day of month.  */
+	  get_number (1, 31);
+	  tm->tm_mday = val;
+	  break;
+	case 'D':
+	  /* Match standard day format.  */
+	  recursive ("%m/%d/%y");
+	  break;
+	case 'H':
+	  /* Match hour in 24-hour clock.  */
+	  get_number (0, 23);
+	  tm->tm_hour = val;
+	  have_I = 0;
+	  break;
+	case 'I':
+	  /* Match hour in 12-hour clock.  */
+	  get_number (1, 12);
+	  tm->tm_hour = val - 1;
+	  have_I = 1;
+	  break;
+	case 'j':
+	  /* Match day number of year.  */
+	  get_number (1, 366);
+	  tm->tm_yday = val - 1;
+	  break;
+	case 'm':
+	  /* Match number of month.  */
+	  get_number (1, 12);
+	  tm->tm_mon = val - 1;
+	  break;
+	case 'M':
+	  /* Match minute.  */
+	  get_number (0, 59);
+	  tm->tm_min = val;
+	  break;
+	case 'n':
+	case 't':
+	  /* Match any white space.  */
+	  while (isspace (*rp))
+	    ++rp;
+	  break;
+	case 'p':
+	  /* Match locale's equivalent of AM/PM.  */
+	  if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
+	    break;
+	  if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
+	    {
+	      is_pm = 1;
+	      break;
+	    }
+	  return NULL;
+	case 'r':
+	  recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM));
+	  break;
+	case 'R':
+	  recursive ("%H:%M");
+	  break;
+	case 'S':
+	  get_number (0, 61);
+	  tm->tm_sec = val;
+	  break;
+	case 'T':
+	  recursive ("%H:%M:%S");
+	  break;
+	case 'U':
+	case 'V':
+	case 'W':
+	  get_number (0, 53);
+	  /* XXX This cannot determine any field in TM.  */
+	  break;
+	case 'w':
+	  /* Match number of weekday.  */
+	  get_number (0, 6);
+	  tm->tm_wday = val;
+	  break;
+	case 'x':
+	  recursive (_NL_CURRENT (LC_TIME, D_FMT));
+	  break;
+	case 'X':
+	  recursive (_NL_CURRENT (LC_TIME, T_FMT));
+	  break;
+	case 'y':
+	  /* Match year within century.  */
+	  get_number (0, 99);
+	  tm->tm_year = val;
+	  break;
+	case 'Y':
+	  /* Match year including century number.  */
+	  get_number (0, INT_MAX);
+	  tm->tm_year = val - (val >= 2000 ? 2000 : 1900);
+	  break;
+	case 'Z':
+	  /* XXX How to handle this?  */
+	  break;
+	case 'E':
+	  switch (*fmt++)
+	    {
+	    case 'c':
+	      /* Match locale's alternate date and time format.  */
+	      recursive (_NL_CURRENT (LC_TIME, ERA_D_T_FMT));
+	      break;
+	    case 'C':
+	    case 'y':
+	    case 'Y':
+	      /* Match name of base year in locale's alternate
+		 representation.  */
+	      /* XXX This is currently not implemented.  It should
+		 use the value _NL_CURRENT (LC_TIME, ERA) but POSIX
+		 leaves this implementation defined and we haven't
+		 figured out how to do it yet.  */
+	      break;
+	    case 'x':
+	      recursive (_NL_CURRENT (LC_TIME, ERA_D_FMT));
+	      break;
+	    case 'X':
+	      recursive (_NL_CURRENT (LC_TIME, ERA_T_FMT));
+	      break;
+	    default:
+	      return NULL;
+	    }
+	  break;
+	case 'O':
+	  switch (*fmt++)
+	    {
+	    case 'd':
+	    case 'e':
+	      /* Match day of month using alternate numeric symbols.  */
+	      get_alt_number (1, 31);
+	      tm->tm_mday = val;
+	      break;
+	    case 'H':
+	      /* Match hour in 24-hour clock using alternate numeric
+		 symbols.  */
+	      get_alt_number (0, 23);
+	      tm->tm_hour = val;
+	      have_I = 0;
+	      break;
+	    case 'I':
+	      /* Match hour in 12-hour clock using alternate numeric
+		 symbols.  */
+	      get_alt_number (1, 12);
+	      tm->tm_hour = val - 1;
+	      have_I = 1;
+	      break;
+	    case 'm':
+	      /* Match month using alternate numeric symbols.  */
+	      get_alt_number (1, 12);
+	      tm->tm_mon = val - 1;
+	      break;
+	    case 'M':
+	      /* Match minutes using alternate numeric symbols.  */
+	      get_alt_number (0, 59);
+	      tm->tm_min = val;
+	      break;
+	    case 'S':
+	      /* Match seconds using alternate numeric symbols.  */
+	      get_alt_number (0, 61);
+	      tm->tm_sec = val;
+	      break;
+	    case 'U':
+	    case 'V':
+	    case 'W':
+	      get_alt_number (0, 53);
+	      /* XXX This cannot determine any field in TM.  */
+	      break;
+	    case 'w':
+	      /* Match number of weekday using alternate numeric symbols.  */
+	      get_alt_number (0, 6);
+	      tm->tm_wday = val;
+	      break;
+	    case 'y':
+	      /* Match year within century using alternate numeric symbols.  */
+	      get_alt_number (0, 99);
+	      break;
+	    default:
+	      return NULL;
+	    }
+	  break;
+	default:
+	  return NULL;
+	}
+    }
+
+  if (have_I && is_pm)
+    tm->tm_hour += 12;
+  
+  return (char *) rp;
+}