Blame time/getdate.c

Packit 6c4009
/* Convert a string representation of time to a time value.
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <alloca.h>
Packit 6c4009
Packit 6c4009
#define TM_YEAR_BASE 1900
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static int first_wday (int year, int mon, int wday);
Packit 6c4009
static int check_mday (int year, int mon, int mday);
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Set to one of the following values to indicate an error.
Packit 6c4009
     1  the DATEMSK environment variable is null or undefined,
Packit 6c4009
     2  the template file cannot be opened for reading,
Packit 6c4009
     3  failed to get file status information,
Packit 6c4009
     4  the template file is not a regular file,
Packit 6c4009
     5  an error is encountered while reading the template file,
Packit 6c4009
     6  memory allication failed (not enough memory available),
Packit 6c4009
     7  there is no line in the template that matches the input,
Packit 6c4009
     8  invalid input specification Example: February 31 or a time is
Packit 6c4009
	specified that can not be represented in a time_t (representing
Packit 6c4009
	the time in seconds since 00:00:00 UTC, January 1, 1970) */
Packit 6c4009
int getdate_err;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Returns the first weekday WDAY of month MON in the year YEAR.  */
Packit 6c4009
static int
Packit 6c4009
first_wday (int year, int mon, int wday)
Packit 6c4009
{
Packit 6c4009
  struct tm tm;
Packit 6c4009
Packit 6c4009
  if (wday == INT_MIN)
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  memset (&tm, 0, sizeof (struct tm));
Packit 6c4009
  tm.tm_year = year;
Packit 6c4009
  tm.tm_mon = mon;
Packit 6c4009
  tm.tm_mday = 1;
Packit 6c4009
  mktime (&tm;;
Packit 6c4009
Packit 6c4009
  return (1 + (wday - tm.tm_wday + 7) % 7);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Returns 1 if MDAY is a valid day of the month in month MON of year
Packit 6c4009
   YEAR, and 0 if it is not.  */
Packit 6c4009
static int
Packit 6c4009
check_mday (int year, int mon, int mday)
Packit 6c4009
{
Packit 6c4009
  switch (mon)
Packit 6c4009
    {
Packit 6c4009
    case 0:
Packit 6c4009
    case 2:
Packit 6c4009
    case 4:
Packit 6c4009
    case 6:
Packit 6c4009
    case 7:
Packit 6c4009
    case 9:
Packit 6c4009
    case 11:
Packit 6c4009
      if (mday >= 1 && mday <= 31)
Packit 6c4009
	return 1;
Packit 6c4009
      break;
Packit 6c4009
    case 3:
Packit 6c4009
    case 5:
Packit 6c4009
    case 8:
Packit 6c4009
    case 10:
Packit 6c4009
      if (mday >= 1 && mday <= 30)
Packit 6c4009
	return 1;
Packit 6c4009
      break;
Packit 6c4009
    case 1:
Packit 6c4009
      if (mday >= 1 && mday <= (__isleap (year) ? 29 : 28))
Packit 6c4009
	return 1;
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__getdate_r (const char *string, struct tm *tp)
Packit 6c4009
{
Packit 6c4009
  FILE *fp;
Packit 6c4009
  char *line;
Packit 6c4009
  size_t len;
Packit 6c4009
  char *datemsk;
Packit 6c4009
  char *result = NULL;
Packit 6c4009
  time_t timer;
Packit 6c4009
  struct tm tm;
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  int mday_ok = 0;
Packit 6c4009
Packit 6c4009
  datemsk = getenv ("DATEMSK");
Packit 6c4009
  if (datemsk == NULL || *datemsk == '\0')
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  if (stat64 (datemsk, &st) < 0)
Packit 6c4009
    return 3;
Packit 6c4009
Packit 6c4009
  if (!S_ISREG (st.st_mode))
Packit 6c4009
    return 4;
Packit 6c4009
Packit 6c4009
  if (__access (datemsk, R_OK) < 0)
Packit 6c4009
    return 2;
Packit 6c4009
Packit 6c4009
  /* Open the template file.  */
Packit 6c4009
  fp = fopen (datemsk, "rce");
Packit 6c4009
  if (fp == NULL)
Packit 6c4009
    return 2;
Packit 6c4009
Packit 6c4009
  /* No threads reading this stream.  */
Packit 6c4009
  __fsetlocking (fp, FSETLOCKING_BYCALLER);
Packit 6c4009
Packit 6c4009
  /* Skip leading whitespace.  */
Packit 6c4009
  while (isspace (*string))
Packit 6c4009
    string++;
Packit 6c4009
Packit 6c4009
  size_t inlen, oldlen;
Packit 6c4009
Packit 6c4009
  oldlen = inlen = strlen (string);
Packit 6c4009
Packit 6c4009
  /* Skip trailing whitespace.  */
Packit 6c4009
  while (inlen > 0 && isspace (string[inlen - 1]))
Packit 6c4009
    inlen--;
Packit 6c4009
Packit 6c4009
  char *instr = NULL;
Packit 6c4009
Packit 6c4009
  if (inlen < oldlen)
Packit 6c4009
    {
Packit 6c4009
      bool using_malloc = false;
Packit 6c4009
Packit 6c4009
      if (__libc_use_alloca (inlen + 1))
Packit 6c4009
	instr = alloca (inlen + 1);
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  instr = malloc (inlen + 1);
Packit 6c4009
	  if (instr == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      fclose (fp);
Packit 6c4009
	      return 6;
Packit 6c4009
	    }
Packit 6c4009
	  using_malloc = true;
Packit 6c4009
	}
Packit 6c4009
      memcpy (instr, string, inlen);
Packit 6c4009
      instr[inlen] = '\0';
Packit 6c4009
      string = instr;
Packit 6c4009
Packit 6c4009
      if (!using_malloc)
Packit 6c4009
	instr = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  line = NULL;
Packit 6c4009
  len = 0;
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      ssize_t n;
Packit 6c4009
Packit 6c4009
      n = __getline (&line, &len, fp);
Packit 6c4009
      if (n < 0)
Packit 6c4009
	break;
Packit 6c4009
      if (line[n - 1] == '\n')
Packit 6c4009
	line[n - 1] = '\0';
Packit 6c4009
Packit 6c4009
      /* Do the conversion.  */
Packit 6c4009
      tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
Packit 6c4009
      tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
Packit 6c4009
      tp->tm_isdst = -1;
Packit 6c4009
      tp->tm_gmtoff = 0;
Packit 6c4009
      tp->tm_zone = NULL;
Packit 6c4009
      result = strptime (string, line, tp);
Packit 6c4009
      if (result && *result == '\0')
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
  while (!__feof_unlocked (fp));
Packit 6c4009
Packit 6c4009
  free (instr);
Packit 6c4009
Packit 6c4009
  /* Free the buffer.  */
Packit 6c4009
  free (line);
Packit 6c4009
Packit 6c4009
  /* Check for errors. */
Packit 6c4009
  if (__ferror_unlocked (fp))
Packit 6c4009
    {
Packit 6c4009
      fclose (fp);
Packit 6c4009
      return 5;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Close template file.  */
Packit 6c4009
  fclose (fp);
Packit 6c4009
Packit 6c4009
  if (result == NULL || *result != '\0')
Packit 6c4009
    return 7;
Packit 6c4009
Packit 6c4009
  /* Get current time.  */
Packit 6c4009
  time (&timer;;
Packit 6c4009
  __localtime_r (&timer, &tm;;
Packit 6c4009
Packit 6c4009
  /* If only the weekday is given, today is assumed if the given day
Packit 6c4009
     is equal to the current day and next week if it is less.  */
Packit 6c4009
  if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
Packit 6c4009
      && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
Packit 6c4009
    {
Packit 6c4009
      tp->tm_year = tm.tm_year;
Packit 6c4009
      tp->tm_mon = tm.tm_mon;
Packit 6c4009
      tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
Packit 6c4009
      mday_ok = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If only the month is given, the current month is assumed if the
Packit 6c4009
     given month is equal to the current month and next year if it is
Packit 6c4009
     less and no year is given (the first day of month is assumed if
Packit 6c4009
     no day is given.  */
Packit 6c4009
  if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
Packit 6c4009
    {
Packit 6c4009
      if (tp->tm_year == INT_MIN)
Packit 6c4009
	tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
Packit 6c4009
      tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
Packit 6c4009
      mday_ok = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If no hour, minute and second are given the current hour, minute
Packit 6c4009
     and second are assumed.  */
Packit 6c4009
  if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
Packit 6c4009
    {
Packit 6c4009
      tp->tm_hour = tm.tm_hour;
Packit 6c4009
      tp->tm_min = tm.tm_min;
Packit 6c4009
      tp->tm_sec = tm.tm_sec;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Fill in the gaps.  */
Packit 6c4009
  if (tp->tm_hour == INT_MIN)
Packit 6c4009
    tp->tm_hour = 0;
Packit 6c4009
  if (tp->tm_min == INT_MIN)
Packit 6c4009
    tp->tm_min = 0;
Packit 6c4009
  if (tp->tm_sec == INT_MIN)
Packit 6c4009
    tp->tm_sec = 0;
Packit 6c4009
Packit 6c4009
  /* If no date is given, today is assumed if the given hour is
Packit 6c4009
     greater than the current hour and tomorrow is assumed if
Packit 6c4009
     it is less.  */
Packit 6c4009
  if (tp->tm_hour >= 0 && tp->tm_hour <= 23
Packit 6c4009
      && tp->tm_mon == INT_MIN
Packit 6c4009
      && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
Packit 6c4009
    {
Packit 6c4009
      tp->tm_mon = tm.tm_mon;
Packit 6c4009
      tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
Packit 6c4009
      mday_ok = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* More fillers.  */
Packit 6c4009
  if (tp->tm_year == INT_MIN)
Packit 6c4009
    tp->tm_year = tm.tm_year;
Packit 6c4009
  if (tp->tm_mon == INT_MIN)
Packit 6c4009
    tp->tm_mon = tm.tm_mon;
Packit 6c4009
Packit 6c4009
  /* Check if the day of month is within range, and if the time can be
Packit 6c4009
     represented in a time_t.  We make use of the fact that the mktime
Packit 6c4009
     call normalizes the struct tm.  */
Packit 6c4009
  if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
Packit 6c4009
				tp->tm_mday))
Packit 6c4009
      || mktime (tp) == (time_t) -1)
Packit 6c4009
    return 8;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#ifdef weak_alias
Packit 6c4009
weak_alias (__getdate_r, getdate_r)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct tm *
Packit 6c4009
getdate (const char *string)
Packit 6c4009
{
Packit 6c4009
  /* Buffer returned by getdate.  */
Packit 6c4009
  static struct tm tmbuf;
Packit 6c4009
  int errval = __getdate_r (string, &tmbuf);
Packit 6c4009
Packit 6c4009
  if (errval != 0)
Packit 6c4009
    {
Packit 6c4009
      getdate_err = errval;
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return &tmbuf;
Packit 6c4009
}