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