1 /*
2  *  C level Utilities for lubridate
3  *
4  *  Author: Vitalie Spinu
5  *  Copyright (C) 2013--2018  Vitalie Spinu, Garrett Grolemund, Hadley Wickham,
6  *
7  *  This program is free software; you can redistribute it and/or modify it
8  *  under the terms of the GNU General Public License as published by the Free
9  *  Software Foundation; either version 2 of the License, or (at your option)
10  *  any later version.
11  *
12  *  This program is distributed in the hope that it will be useful, but WITHOUT
13  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  *  more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, a copy is available at
19  *  http://www.r-project.org/Licenses/
20  */
21 
22 
23 #include "constants.h"
24 #include "utils.h"
25 #include <stdio.h>
26 #include <stdbool.h> // for bool type
27 #include <ctype.h>
28 #include <Rinternals.h>
29 
30 // return adjustment (in seconds) due to leap years
31 // y: years after (positive) or before (negative) 2000-01-01 00:00:00
adjust_leap_years(int y,int m,int is_leap)32 int adjust_leap_years(int y, int m, int is_leap){
33   int SECS = 0;
34 
35   if ( y >= 0 ){
36 	// ordinary leap days after 2000-01-01 00:00:00
37 	SECS += ( y / 4 ) * daylen + daylen;
38 	if( y > 99 )
39 	  SECS += (y / 400 - y / 100) * daylen;
40 	// adjust if within a leap year
41 	if ( is_leap && m < 3 )
42 	  SECS -= daylen;
43   } else {
44 	SECS += (y / 4) * daylen;
45 	if( y < -99 )
46 	  SECS += (y / 400 - y / 100) * daylen;
47 	if ( is_leap && m > 2 )
48 	  SECS += daylen;
49   }
50 
51   return SECS;
52 }
53 
54 // check if d makes sense in this month
check_mdays(int m,int d,int is_leap)55 int check_mdays(int m, int d, int is_leap){
56 
57   int succeed = 1;
58 
59   if ( m == 2 ){
60 	// no check for d > 0 because we allow missing days in parsing
61 	if ( is_leap )
62 	  succeed = d < 30;
63 	else
64 	  succeed = d < 29;
65   } else {
66 	succeed = d <= mdays[m];
67   }
68 
69   return succeed;
70 }
71 
72 // parse fractional part
parse_fractional(const char ** c)73 double parse_fractional(const char **c) {
74   double out = 0.0, factor = 0.1;
75   while (DIGIT(**c)) { out = out + (**c - '0')*factor; factor *= 0.1; (*c)++; }
76   return out;
77 }
78 
79 /* parse N digit characters from **c. Return parsed non-negative integer. If
80    failed to pass N chars, return -1.*/
parse_int(const char ** c,const int N,const int strict)81 int parse_int (const char **c, const int N, const int strict) {
82   int tN = N, X = 0;
83   while (DIGIT(**c) && tN > 0) {
84     X = X * 10 + (**c - '0');
85     (*c)++;
86     tN--;
87   }
88   if (strict && tN > 0) return -1; // not all numbers have been consumed
89   else if (tN == N) return -1; // no parsing happened
90   else return X;
91 }
92 
93 
94 // Find maximal partial match in `strings`.
95 //
96 // Increment *c and return index in 0..(length(strings)-1) if match was found,
97 // -1 if not. Matching starts from *c, with all non-alpha-numeric characters
98 // pre-skipped.
99 //
100 // - *c: pointer to a character in a C string (incremented by side effect)
101 // - *stings: pointer to an array of C strings to be matched to
102 // - strings_len: length of strings array
parse_alphanum(const char ** c,const char ** strings,const int strings_len,const char ignore_case)103 int parse_alphanum(const char **c, const char **strings, const int strings_len, const char ignore_case){
104 
105   // tracking array: all valid objects are marked with 1, invalid with 0
106   bool *track = (bool *)R_alloc(strings_len, sizeof(bool));
107   for (int i = 0; i < strings_len; i++){
108     track[i] = 1;
109   }
110 
111   int j = 0, out = -1, good_tracks = strings_len;
112   while (**c && !ALPHA(**c) && !DIGIT(**c)) (*c)++;
113 
114   while (**c && good_tracks) {
115     // stop when all tracks have been exhausted
116     for (int i = 0; i < strings_len; i++){
117 
118       // keep going while at least one valid track
119       if (track[i]){
120 
121         if (strings[i][j]) {
122           if (**c == strings[i][j] || (ignore_case && (tolower(**c) == strings[i][j]))) {
123             out = i;
124           } else { // invalidate track i if not matching
125             track[i] = 0;
126             good_tracks--;
127           }
128         } else { // reached to the end of string i; return it if the last track
129           good_tracks--;
130           out = i;
131         }
132       }
133     }
134     if (good_tracks) {
135       (*c)++;
136       j++;
137     }
138   }
139   return out;
140 }
141