Blame posixtm.c

Packit 081926
/*
Packit 081926
 * This file is taken from coreutils 7.4
Packit 081926
 */
Packit 081926
Packit 081926
/* Parse dates for touch and date.
Packit 081926
Packit 081926
   Copyright (C) 1989, 1990, 1991, 1998, 2000, 2001, 2002, 2003, 2004,
Packit 081926
   2005, 2006, 2007 Free Software Foundation Inc.
Packit 081926
Packit 081926
   This program is free software: you can redistribute it and/or modify
Packit 081926
   it under the terms of the GNU General Public License as published by
Packit 081926
   the Free Software Foundation; either version 3 of the License, or
Packit 081926
   (at your option) any later version.
Packit 081926
Packit 081926
   This program is distributed in the hope that it will be useful,
Packit 081926
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 081926
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 081926
   GNU General Public License for more details.
Packit 081926
Packit 081926
   You should have received a copy of the GNU General Public License
Packit 081926
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 081926
Packit 081926
/* Yacc-based version written by Jim Kingdon and David MacKenzie.
Packit 081926
   Rewritten by Jim Meyering.  */
Packit 081926
Packit 081926
#include <config.h>
Packit 081926
Packit 081926
#include "posixtm.h"
Packit 081926
Packit 081926
#include <stdio.h>
Packit 081926
#include <stdlib.h>
Packit 081926
#include <sys/types.h>
Packit 081926
#include <string.h>
Packit 081926
Packit 081926
#if USE_UNLOCKED_IO
Packit 081926
# include "unlocked-io.h"
Packit 081926
#endif
Packit 081926
Packit 081926
/* ISDIGIT differs from isdigit, as follows:
Packit 081926
   - Its arg may be any int or unsigned int; it need not be an unsigned char
Packit 081926
     or EOF.
Packit 081926
   - It's typically faster.
Packit 081926
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
Packit 081926
   isdigit unless it's important to use the locale's definition
Packit 081926
   of `digit' even when the host does not conform to POSIX.  */
Packit 081926
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
Packit 081926
Packit 081926
/*
Packit 081926
  POSIX requires:
Packit 081926
Packit 081926
  touch -t [[CC]YY]mmddhhmm[.ss] FILE...
Packit 081926
    8, 10, or 12 digits, followed by optional .ss
Packit 081926
    (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
Packit 081926
Packit 081926
  touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
Packit 081926
    8 or 10 digits, YY (if present) must be in the range 69-99
Packit 081926
    (PDS_TRAILING_YEAR | PDS_PRE_2000)
Packit 081926
Packit 081926
  date mmddhhmm[[CC]YY]
Packit 081926
    8, 10, or 12 digits
Packit 081926
    (PDS_TRAILING_YEAR | PDS_CENTURY)
Packit 081926
Packit 081926
*/
Packit 081926
Packit 081926
static int
Packit 081926
year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
Packit 081926
{
Packit 081926
  switch (n)
Packit 081926
    {
Packit 081926
    case 1:
Packit 081926
      tm->tm_year = *digit_pair;
Packit 081926
      /* Deduce the century based on the year.
Packit 081926
	 POSIX requires that 00-68 be interpreted as 2000-2068,
Packit 081926
	 and that 69-99 be interpreted as 1969-1999.  */
Packit 081926
      if (digit_pair[0] <= 68)
Packit 081926
	{
Packit 081926
	  if (syntax_bits & PDS_PRE_2000)
Packit 081926
	    return 1;
Packit 081926
	  tm->tm_year += 100;
Packit 081926
	}
Packit 081926
      break;
Packit 081926
Packit 081926
    case 2:
Packit 081926
      if (! (syntax_bits & PDS_CENTURY))
Packit 081926
	return 1;
Packit 081926
      tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
Packit 081926
      break;
Packit 081926
Packit 081926
    case 0:
Packit 081926
      {
Packit 081926
	time_t now;
Packit 081926
	struct tm *tmp;
Packit 081926
Packit 081926
	/* Use current year.  */
Packit 081926
	time (&now;;
Packit 081926
	tmp = localtime (&now;;
Packit 081926
	if (! tmp)
Packit 081926
	  return 1;
Packit 081926
	tm->tm_year = tmp->tm_year;
Packit 081926
      }
Packit 081926
      break;
Packit 081926
Packit 081926
    default:
Packit 081926
      abort ();
Packit 081926
    }
Packit 081926
Packit 081926
  return 0;
Packit 081926
}
Packit 081926
Packit 081926
static int
Packit 081926
posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
Packit 081926
{
Packit 081926
  const char *dot = NULL;
Packit 081926
  int pair[6];
Packit 081926
  int *p;
Packit 081926
  size_t i;
Packit 081926
Packit 081926
  size_t s_len = strlen (s);
Packit 081926
  size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
Packit 081926
		? (size_t) (dot - s)
Packit 081926
		: s_len);
Packit 081926
Packit 081926
  if (len != 8 && len != 10 && len != 12)
Packit 081926
    return 1;
Packit 081926
Packit 081926
  if (dot)
Packit 081926
    {
Packit 081926
      if (!(syntax_bits & PDS_SECONDS))
Packit 081926
	return 1;
Packit 081926
Packit 081926
      if (s_len - len != 3)
Packit 081926
	return 1;
Packit 081926
    }
Packit 081926
Packit 081926
  for (i = 0; i < len; i++)
Packit 081926
    if (!ISDIGIT (s[i]))
Packit 081926
      return 1;
Packit 081926
Packit 081926
  len /= 2;
Packit 081926
  for (i = 0; i < len; i++)
Packit 081926
    pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
Packit 081926
Packit 081926
  p = pair;
Packit 081926
  if (syntax_bits & PDS_LEADING_YEAR)
Packit 081926
    {
Packit 081926
      if (year (tm, p, len - 4, syntax_bits))
Packit 081926
	return 1;
Packit 081926
      p += len - 4;
Packit 081926
      len = 4;
Packit 081926
    }
Packit 081926
Packit 081926
  /* Handle 8 digits worth of `MMDDhhmm'.  */
Packit 081926
  tm->tm_mon = *p++ - 1;
Packit 081926
  tm->tm_mday = *p++;
Packit 081926
  tm->tm_hour = *p++;
Packit 081926
  tm->tm_min = *p++;
Packit 081926
  len -= 4;
Packit 081926
Packit 081926
  /* Handle any trailing year.  */
Packit 081926
  if (syntax_bits & PDS_TRAILING_YEAR)
Packit 081926
    {
Packit 081926
      if (year (tm, p, len, syntax_bits))
Packit 081926
	return 1;
Packit 081926
    }
Packit 081926
Packit 081926
  /* Handle seconds.  */
Packit 081926
  if (!dot)
Packit 081926
    {
Packit 081926
      tm->tm_sec = 0;
Packit 081926
    }
Packit 081926
  else
Packit 081926
    {
Packit 081926
      int seconds;
Packit 081926
Packit 081926
      ++dot;
Packit 081926
      if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
Packit 081926
	return 1;
Packit 081926
      seconds = 10 * (dot[0] - '0') + dot[1] - '0';
Packit 081926
Packit 081926
      tm->tm_sec = seconds;
Packit 081926
    }
Packit 081926
Packit 081926
  return 0;
Packit 081926
}
Packit 081926
Packit 081926
/* Parse a POSIX-style date, returning true if successful.  */
Packit 081926
Packit 081926
bool
Packit 081926
posixtime (time_t *p, const char *s, unsigned int syntax_bits)
Packit 081926
{
Packit 081926
  struct tm tm0;
Packit 081926
  struct tm tm1;
Packit 081926
  struct tm const *tm;
Packit 081926
  time_t t;
Packit 081926
Packit 081926
  if (posix_time_parse (&tm0, s, syntax_bits))
Packit 081926
    return false;
Packit 081926
Packit 081926
  tm1 = tm0;
Packit 081926
  tm1.tm_isdst = -1;
Packit 081926
  t = mktime (&tm1);
Packit 081926
Packit 081926
  if (t != (time_t) -1)
Packit 081926
    tm = &tm;;
Packit 081926
  else
Packit 081926
    {
Packit 081926
      /* mktime returns -1 for errors, but -1 is also a valid time_t
Packit 081926
	 value.  Check whether an error really occurred.  */
Packit 081926
      tm = localtime (&t);
Packit 081926
      if (! tm)
Packit 081926
	return false;
Packit 081926
    }
Packit 081926
Packit 081926
  /* Reject dates like "September 31" and times like "25:61".  */
Packit 081926
  if ((tm0.tm_year ^ tm->tm_year)
Packit 081926
      | (tm0.tm_mon ^ tm->tm_mon)
Packit 081926
      | (tm0.tm_mday ^ tm->tm_mday)
Packit 081926
      | (tm0.tm_hour ^ tm->tm_hour)
Packit 081926
      | (tm0.tm_min ^ tm->tm_min)
Packit 081926
      | (tm0.tm_sec ^ tm->tm_sec))
Packit 081926
    return false;
Packit 081926
Packit 081926
  *p = t;
Packit 081926
  return true;
Packit 081926
}
Packit 081926
Packit 081926
#ifdef TEST_POSIXTIME
Packit 081926
/*
Packit 081926
    Test mainly with syntax_bits == 13
Packit 081926
    (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
Packit 081926
Packit 081926
    This test data assumes Universal Time, e.g., TZ="UTC0".
Packit 081926
Packit 081926
    This test data also assumes that time_t is signed and is at least
Packit 081926
    39 bits wide, so that it can represent all years from 0000 through
Packit 081926
    9999.  A host with 32-bit signed time_t can represent only time
Packit 081926
    stamps in the range 1901-12-13 20:45:52 through 2038-01-18
Packit 081926
    03:14:07 UTC, assuming POSIX time_t with no leap seconds, so test
Packit 081926
    cases outside this range will not work on such a host.
Packit 081926
Packit 081926
    Also, the first two lines of test data assume that the current
Packit 081926
    year is 2002.
Packit 081926
Packit 081926
BEGIN-DATA
Packit 081926
12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
Packit 081926
12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
Packit 081926
000001010000.00 13 -62167132800 Sun Jan  1 00:00:00 0000
Packit 081926
190112132045.52 13  -2147483648 Fri Dec 13 20:45:52 1901
Packit 081926
190112132045.53 13  -2147483647 Fri Dec 13 20:45:53 1901
Packit 081926
190112132046.52 13  -2147483588 Fri Dec 13 20:46:52 1901
Packit 081926
190112132145.52 13  -2147480048 Fri Dec 13 21:45:52 1901
Packit 081926
190112142045.52 13  -2147397248 Sat Dec 14 20:45:52 1901
Packit 081926
190201132045.52 13  -2144805248 Mon Jan 13 20:45:52 1902
Packit 081926
196912312359.59 13           -1 Wed Dec 31 23:59:59 1969
Packit 081926
197001010000.00 13            0 Thu Jan  1 00:00:00 1970
Packit 081926
197001010000.01 13            1 Thu Jan  1 00:00:01 1970
Packit 081926
197001010001.00 13           60 Thu Jan  1 00:01:00 1970
Packit 081926
197001010100.00 13         3600 Thu Jan  1 01:00:00 1970
Packit 081926
197001020000.00 13        86400 Fri Jan  2 00:00:00 1970
Packit 081926
197002010000.00 13      2678400 Sun Feb  1 00:00:00 1970
Packit 081926
197101010000.00 13     31536000 Fri Jan  1 00:00:00 1971
Packit 081926
197001000000.00 13            * *
Packit 081926
197000010000.00 13            * *
Packit 081926
197001010000.60 13            * *
Packit 081926
197001010060.00 13            * *
Packit 081926
197001012400.00 13            * *
Packit 081926
197001320000.00 13            * *
Packit 081926
197013010000.00 13            * *
Packit 081926
203801190314.06 13   2147483646 Tue Jan 19 03:14:06 2038
Packit 081926
203801190314.07 13   2147483647 Tue Jan 19 03:14:07 2038
Packit 081926
203801190314.08 13   2147483648 Tue Jan 19 03:14:08 2038
Packit 081926
999912312359.59 13 253402300799 Fri Dec 31 23:59:59 9999
Packit 081926
1112131415      13   1323785700 Tue Dec 13 14:15:00 2011
Packit 081926
1112131415.16   13   1323785716 Tue Dec 13 14:15:16 2011
Packit 081926
201112131415.16 13   1323785716 Tue Dec 13 14:15:16 2011
Packit 081926
191112131415.16 13  -1831974284 Wed Dec 13 14:15:16 1911
Packit 081926
203712131415.16 13   2144326516 Sun Dec 13 14:15:16 2037
Packit 081926
3712131415.16   13   2144326516 Sun Dec 13 14:15:16 2037
Packit 081926
6812131415.16   13   3122633716 Thu Dec 13 14:15:16 2068
Packit 081926
6912131415.16   13     -1590284 Sat Dec 13 14:15:16 1969
Packit 081926
7012131415.16   13     29945716 Sun Dec 13 14:15:16 1970
Packit 081926
1213141599       2    945094500 Mon Dec 13 14:15:00 1999
Packit 081926
1213141500       2    976716900 Wed Dec 13 14:15:00 2000
Packit 081926
END-DATA
Packit 081926
Packit 081926
*/
Packit 081926
Packit 081926
# define MAX_BUFF_LEN 1024
Packit 081926
Packit 081926
int
Packit 081926
main (void)
Packit 081926
{
Packit 081926
  char buff[MAX_BUFF_LEN + 1];
Packit 081926
Packit 081926
  buff[MAX_BUFF_LEN] = 0;
Packit 081926
  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
Packit 081926
    {
Packit 081926
      char time_str[MAX_BUFF_LEN];
Packit 081926
      unsigned int syntax_bits;
Packit 081926
      time_t t;
Packit 081926
      if (sscanf (buff, "%s %u", time_str, &syntax_bits) != 2)
Packit 081926
	printf ("*\n");
Packit 081926
      else
Packit 081926
	{
Packit 081926
	  printf ("%-15s %2u ", time_str, syntax_bits);
Packit 081926
	  if (posixtime (&t, time_str, syntax_bits))
Packit 081926
	    printf ("%12ld %s", (long int) t, ctime (&t);;
Packit 081926
	  else
Packit 081926
	    printf ("%12s %s", "*", "*\n");
Packit 081926
	}
Packit 081926
    }
Packit 081926
  exit (0);
Packit 081926
Packit 081926
}
Packit 081926
#endif
Packit 081926

Packit 081926
/*
Packit 081926
Local Variables:
Packit 081926
compile-command: "gcc -DTEST_POSIXTIME -g -O -Wall -W posixtm.c"
Packit 081926
End:
Packit 081926
*/