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