(root)/
glibc-2.38/
time/
getdate.c
       1  /* Convert a string representation of time to a time value.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <limits.h>
      20  #include <stdio.h>
      21  #include <stdio_ext.h>
      22  #include <stdlib.h>
      23  #include <stdbool.h>
      24  #include <string.h>
      25  #include <time.h>
      26  #include <unistd.h>
      27  #include <sys/stat.h>
      28  #include <ctype.h>
      29  
      30  #define TM_YEAR_BASE 1900
      31  
      32  
      33  /* Prototypes for local functions.  */
      34  static int first_wday (int year, int mon, int wday);
      35  static int check_mday (int year, int mon, int mday);
      36  
      37  
      38  /* Set to one of the following values to indicate an error.
      39       1  the DATEMSK environment variable is null or undefined,
      40       2  the template file cannot be opened for reading,
      41       3  failed to get file status information,
      42       4  the template file is not a regular file,
      43       5  an error is encountered while reading the template file,
      44       6  memory allication failed (not enough memory available),
      45       7  there is no line in the template that matches the input,
      46       8  invalid input specification Example: February 31 or a time is
      47  	specified that can not be represented in a time_t (representing
      48  	the time in seconds since 00:00:00 UTC, January 1, 1970) */
      49  int getdate_err;
      50  
      51  
      52  /* Returns the first weekday WDAY of month MON in the year YEAR.  */
      53  static int
      54  first_wday (int year, int mon, int wday)
      55  {
      56    struct tm tm;
      57  
      58    if (wday == INT_MIN)
      59      return 1;
      60  
      61    memset (&tm, 0, sizeof (struct tm));
      62    tm.tm_year = year;
      63    tm.tm_mon = mon;
      64    tm.tm_mday = 1;
      65    mktime (&tm);
      66  
      67    return (1 + (wday - tm.tm_wday + 7) % 7);
      68  }
      69  
      70  
      71  /* Returns 1 if MDAY is a valid day of the month in month MON of year
      72     YEAR, and 0 if it is not.  */
      73  static int
      74  check_mday (int year, int mon, int mday)
      75  {
      76    switch (mon)
      77      {
      78      case 0:
      79      case 2:
      80      case 4:
      81      case 6:
      82      case 7:
      83      case 9:
      84      case 11:
      85        if (mday >= 1 && mday <= 31)
      86  	return 1;
      87        break;
      88      case 3:
      89      case 5:
      90      case 8:
      91      case 10:
      92        if (mday >= 1 && mday <= 30)
      93  	return 1;
      94        break;
      95      case 1:
      96        if (mday >= 1 && mday <= (__isleap (year) ? 29 : 28))
      97  	return 1;
      98        break;
      99      }
     100  
     101    return 0;
     102  }
     103  
     104  
     105  int
     106  __getdate_r (const char *string, struct tm *tp)
     107  {
     108    FILE *fp;
     109    char *line;
     110    size_t len;
     111    char *datemsk;
     112    char *result = NULL;
     113    __time64_t timer;
     114    struct tm tm;
     115    struct __stat64_t64 st;
     116    bool mday_ok = false;
     117    bool found = false;
     118  
     119    datemsk = getenv ("DATEMSK");
     120    if (datemsk == NULL || *datemsk == '\0')
     121      return 1;
     122  
     123    if (__stat64_time64 (datemsk, &st) < 0)
     124      return 3;
     125  
     126    if (!S_ISREG (st.st_mode))
     127      return 4;
     128  
     129    if (__access (datemsk, R_OK) < 0)
     130      return 2;
     131  
     132    /* Open the template file.  */
     133    fp = fopen (datemsk, "rce");
     134    if (fp == NULL)
     135      return 2;
     136  
     137    /* No threads reading this stream.  */
     138    __fsetlocking (fp, FSETLOCKING_BYCALLER);
     139  
     140    /* Skip leading whitespace.  */
     141    while (isspace (*string))
     142      string++;
     143  
     144    size_t inlen, oldlen;
     145  
     146    oldlen = inlen = strlen (string);
     147  
     148    /* Skip trailing whitespace.  */
     149    while (inlen > 0 && isspace (string[inlen - 1]))
     150      inlen--;
     151  
     152    char *instr = NULL;
     153  
     154    if (inlen < oldlen)
     155      {
     156        instr = __strndup(string, inlen);
     157        if (instr == NULL)
     158  	{
     159  	  fclose(fp);
     160  	  return 6;
     161  	}
     162  
     163        string = instr;
     164      }
     165  
     166    line = NULL;
     167    len = 0;
     168    do
     169      {
     170        ssize_t n;
     171  
     172        n = __getline (&line, &len, fp);
     173        if (n < 0)
     174  	break;
     175        if (line[n - 1] == '\n')
     176  	line[n - 1] = '\0';
     177  
     178        /* Do the conversion.  */
     179        tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
     180        tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
     181        tp->tm_isdst = -1;
     182        tp->tm_gmtoff = 0;
     183        tp->tm_zone = NULL;
     184        result = strptime (string, line, tp);
     185        if ((found = (result && *result == '\0')))
     186  	break;
     187      }
     188    while (!__feof_unlocked (fp));
     189  
     190    free (instr);
     191  
     192    /* Free the buffer.  */
     193    free (line);
     194  
     195    /* Check for errors. */
     196    if (__ferror_unlocked (fp))
     197      {
     198        fclose (fp);
     199        return 5;
     200      }
     201  
     202    /* Close template file.  */
     203    fclose (fp);
     204  
     205    if (!found)
     206      return 7;
     207  
     208    /* Get current time.  */
     209    timer = time64_now ();
     210    __localtime64_r (&timer, &tm);
     211  
     212    /* If only the weekday is given, today is assumed if the given day
     213       is equal to the current day and next week if it is less.  */
     214    if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
     215        && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
     216      {
     217        tp->tm_year = tm.tm_year;
     218        tp->tm_mon = tm.tm_mon;
     219        tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
     220        mday_ok = true;
     221      }
     222  
     223    /* If only the month is given, the current month is assumed if the
     224       given month is equal to the current month and next year if it is
     225       less and no year is given (the first day of month is assumed if
     226       no day is given.  */
     227    if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
     228      {
     229        if (tp->tm_year == INT_MIN)
     230  	tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
     231        tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
     232        mday_ok = true;
     233      }
     234  
     235    /* If no hour, minute and second are given the current hour, minute
     236       and second are assumed.  */
     237    if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
     238      {
     239        tp->tm_hour = tm.tm_hour;
     240        tp->tm_min = tm.tm_min;
     241        tp->tm_sec = tm.tm_sec;
     242      }
     243  
     244    /* Fill in the gaps.  */
     245    if (tp->tm_hour == INT_MIN)
     246      tp->tm_hour = 0;
     247    if (tp->tm_min == INT_MIN)
     248      tp->tm_min = 0;
     249    if (tp->tm_sec == INT_MIN)
     250      tp->tm_sec = 0;
     251  
     252    /* If no date is given, today is assumed if the given hour is
     253       greater than the current hour and tomorrow is assumed if
     254       it is less.  */
     255    if (tp->tm_hour >= 0 && tp->tm_hour <= 23
     256        && tp->tm_mon == INT_MIN
     257        && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
     258      {
     259        tp->tm_mon = tm.tm_mon;
     260        tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
     261        mday_ok = 1;
     262      }
     263  
     264    /* More fillers.  */
     265    if (tp->tm_year == INT_MIN)
     266      tp->tm_year = tm.tm_year;
     267    if (tp->tm_mon == INT_MIN)
     268      tp->tm_mon = tm.tm_mon;
     269  
     270    /* Check if the day of month is within range, and if the time can be
     271       represented in a time_t.  We make use of the fact that the mktime
     272       call normalizes the struct tm.  */
     273    if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
     274  				tp->tm_mday))
     275        || __mktime64 (tp) == (time_t) -1)
     276      return 8;
     277  
     278    return 0;
     279  }
     280  weak_alias (__getdate_r, getdate_r)
     281  libc_hidden_def (__getdate_r)
     282  
     283  struct tm *
     284  getdate (const char *string)
     285  {
     286    /* Buffer returned by getdate.  */
     287    static struct tm tmbuf;
     288    int errval = __getdate_r (string, &tmbuf);
     289  
     290    if (errval != 0)
     291      {
     292        getdate_err = errval;
     293        return NULL;
     294      }
     295  
     296    return &tmbuf;
     297  }