LCOV - code coverage report
Current view: top level - lib - lwan-time.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 20 82 24.4 %
Date: 2023-04-18 16:19:03 Functions: 2 5 40.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2017 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program 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
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
      18             :  */
      19             : 
      20             : #define _GNU_SOURCE
      21             : #include <stdio.h>
      22             : #include <errno.h>
      23             : #include <time.h>
      24             : #include <limits.h>
      25             : 
      26             : #include "lwan-private.h"
      27             : #include "int-to-str.h"
      28             : 
      29           0 : static int parse_2_digit_num_no_end_check(const char *str, unsigned int max)
      30             : {
      31             :     static const unsigned int tens[16] = {
      32             :         0, 10, 20, 30, 40, 50, 60, 70, 80, 90, [11 ... 15] = UINT_MAX,
      33             :     };
      34           0 :     const unsigned int n_tens = (unsigned int)(str[0] - '0');
      35           0 :     const unsigned int n_ones = (unsigned int)(str[1] - '0');
      36           0 :     const unsigned int val = tens[n_tens & 15] + n_ones;
      37           0 :     return UNLIKELY(val > max) ? -EINVAL : (int)val;
      38             : }
      39             : 
      40             : static int
      41           0 : parse_2_digit_num(const char *str, const char end_chr, unsigned int max)
      42             : {
      43           0 :     if (UNLIKELY(str[2] != end_chr))
      44           0 :         return -EINVAL;
      45           0 :     return parse_2_digit_num_no_end_check(str, max);
      46             : }
      47             : 
      48           0 : int lwan_parse_rfc_time(const char in[static 30], time_t *out)
      49             : {
      50             :     /* This function is used instead of strptime() because locale
      51             :      * information can affect the parsing.  Instead of defining
      52             :      * the locale to "C", use hardcoded constants. */
      53             :     struct tm tm;
      54           0 :     const char *str = in;
      55             : 
      56           0 :     STRING_SWITCH(str) {
      57           0 :     case STR4_INT('S','u','n',','): tm.tm_wday = 0; break;
      58           0 :     case STR4_INT('M','o','n',','): tm.tm_wday = 1; break;
      59           0 :     case STR4_INT('T','u','e',','): tm.tm_wday = 2; break;
      60           0 :     case STR4_INT('W','e','d',','): tm.tm_wday = 3; break;
      61           0 :     case STR4_INT('T','h','u',','): tm.tm_wday = 4; break;
      62           0 :     case STR4_INT('F','r','i',','): tm.tm_wday = 5; break;
      63           0 :     case STR4_INT('S','a','t',','): tm.tm_wday = 6; break;
      64           0 :     default: return -EINVAL;
      65             :     }
      66           0 :     str += 5;
      67             : 
      68           0 :     tm.tm_mday = parse_2_digit_num(str, ' ', 31);
      69           0 :     if (UNLIKELY(tm.tm_mday <= 0))
      70           0 :         return -EINVAL;
      71           0 :     str += 3;
      72             : 
      73           0 :     STRING_SWITCH(str) {
      74           0 :     case STR4_INT('J','a','n',' '): tm.tm_mon = 0; break;
      75           0 :     case STR4_INT('F','e','b',' '): tm.tm_mon = 1; break;
      76           0 :     case STR4_INT('M','a','r',' '): tm.tm_mon = 2; break;
      77           0 :     case STR4_INT('A','p','r',' '): tm.tm_mon = 3; break;
      78           0 :     case STR4_INT('M','a','y',' '): tm.tm_mon = 4; break;
      79           0 :     case STR4_INT('J','u','n',' '): tm.tm_mon = 5; break;
      80           0 :     case STR4_INT('J','u','l',' '): tm.tm_mon = 6; break;
      81           0 :     case STR4_INT('A','u','g',' '): tm.tm_mon = 7; break;
      82           0 :     case STR4_INT('S','e','p',' '): tm.tm_mon = 8; break;
      83           0 :     case STR4_INT('O','c','t',' '): tm.tm_mon = 9; break;
      84           0 :     case STR4_INT('N','o','v',' '): tm.tm_mon = 10; break;
      85           0 :     case STR4_INT('D','e','c',' '): tm.tm_mon = 11; break;
      86           0 :     default: return -EINVAL;
      87             :     }
      88           0 :     str += 4;
      89             : 
      90           0 :     int year_hundreds = parse_2_digit_num_no_end_check(str, 21);
      91           0 :     int year_ones = parse_2_digit_num_no_end_check(str + 2, 99);
      92           0 :     if (UNLIKELY(year_hundreds < 0 || year_ones < 0))
      93           0 :         return -EINVAL;
      94           0 :     tm.tm_year = (year_hundreds * 100 + year_ones) - 1900;
      95           0 :     if (UNLIKELY(tm.tm_year < 0 || tm.tm_year > 1000))
      96           0 :         return -EINVAL;
      97           0 :     str += 5;
      98             : 
      99           0 :     tm.tm_hour = parse_2_digit_num(str, ':', 23);
     100           0 :     str += 3;
     101           0 :     tm.tm_min = parse_2_digit_num(str, ':', 59);
     102           0 :     str += 3;
     103           0 :     tm.tm_sec = parse_2_digit_num(str, ' ', 59);
     104           0 :     str += 3;
     105             : 
     106           0 :     STRING_SWITCH(str) {
     107           0 :     case STR4_INT('G','M','T','\0'):
     108           0 :         tm.tm_isdst = -1;
     109             : 
     110           0 :         *out = timegm(&tm);
     111             : 
     112           0 :         if (LIKELY(*out > 0))
     113           0 :             return 0;
     114             : 
     115             :         /* Fallthrough */
     116             :     default:
     117           0 :         return -EINVAL;
     118             :     }
     119             : }
     120             : 
     121             : static inline char *
     122        2460 : append_two_digits(char *p, unsigned int digits)
     123             : {
     124        2460 :     return mempcpy(p, uint_to_string_2_digits(digits), 2);
     125             : }
     126             : 
     127         410 : int lwan_format_rfc_time(const time_t in, char out[static 30])
     128             : {
     129             :     static const char *weekdays = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";
     130             :     static const char *months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
     131             :     struct tm tm;
     132             :     char *p;
     133             : 
     134         410 :     if (UNLIKELY(!gmtime_r(&in, &tm)))
     135           0 :         return -errno;
     136             : 
     137         410 :     p = mempcpy(out, weekdays + tm.tm_wday * 4, 4);
     138         410 :     *p++ = ' ';
     139             : 
     140         410 :     p = append_two_digits(p, (unsigned int)tm.tm_mday);
     141         410 :     *p++ = ' ';
     142         410 :     p = mempcpy(p, months + tm.tm_mon * 4, 4);
     143             : 
     144         410 :     tm.tm_year += 1900;
     145         410 :     p = append_two_digits(p, (unsigned int)tm.tm_year / 100);
     146         410 :     p = append_two_digits(p, (unsigned int)tm.tm_year % 100);
     147             : 
     148         410 :     *p++ = ' ';
     149             : 
     150         410 :     p = append_two_digits(p, (unsigned int)tm.tm_hour);
     151         410 :     *p++ = ':';
     152         410 :     p = append_two_digits(p, (unsigned int)tm.tm_min);
     153         410 :     *p++ = ':';
     154         410 :     p = append_two_digits(p, (unsigned int)tm.tm_sec);
     155             : 
     156         410 :     memcpy(p, " GMT", 5);
     157             : 
     158         410 :     return 0;
     159             : }

Generated by: LCOV version 1.15-2-gb9d6727