GRASS 8 Programmer's Manual  8.5.0dev(2025)-c070206eb1
scan.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1995. Bill Brown <brown@gis.uiuc.edu> & Michael Shapiro
3  *
4  * This program is free software under the GPL (>=v2)
5  * Read the file GPL.TXT coming with GRASS for details.
6  */
7 #include <stdio.h>
8 #include <string.h>
9 #include <grass/datetime.h>
10 
11 static int scan_absolute(DateTime *, const char *);
12 static int more(const char **);
13 static int minus_sign(const char **);
14 static int is_bc(const char **);
15 static int is_relative(const char *);
16 static int relative_term(const char **, double *, int *, int *, int *);
17 static int scan_tz(const char *, int *);
18 static int get_word(const char **, char *);
19 static char lowercase(char);
20 static int which_month(const char *, int *);
21 static int scan_relative(DateTime *, const char *);
22 static int is_space(char);
23 static int is_digit(char);
24 static void skip_space(const char **);
25 static int get_int(const char **, int *, int *);
26 static int get_double(const char **, double *, int *, int *);
27 
28 /*!
29  * \brief
30  *
31  * Convert the ascii string
32  * into a DateTime. This determines the mode/from/to based on the string, inits
33  * 'dt' and then sets values in 'dt' based on the [???]
34  * Returns 0 if 'string' is legal, -1 if not.
35  *
36  * \param dt
37  * \param buf
38  * \return int
39  */
40 int datetime_scan(DateTime *dt, const char *buf)
41 {
42  if (is_relative(buf)) {
43  if (scan_relative(dt, buf))
44  return 0;
45  return datetime_error(-1, "Invalid interval datetime format");
46  }
47  if (scan_absolute(dt, buf))
48  return 0;
49  return datetime_error(-2, "Invalid absolute datetime format");
50 }
51 
52 static const char *month_names[] = {"jan", "feb", "mar", "apr", "may", "jun",
53  "jul", "aug", "sep", "oct", "nov", "dec"};
54 
55 static int scan_absolute(DateTime *dt, const char *buf)
56 {
57  char word[1024];
58  int n;
59  int ndigits;
60  int tz = 0;
61  int have_tz = 0;
62  int bc = 0;
63  int to, fracsec = 0;
64  int year, month, day = 0, hour, minute;
65  double second;
66  const char *p;
67 
68  p = buf;
69  if (!more(&p))
70  return 0;
71 
72  if (!get_int(&p, &n, &ndigits)) { /* no day, so must be month, like Jan */
73  if (!get_word(&p, word))
74  return 0;
75  if (!which_month(word, &month))
76  return 0;
77  if (!get_int(&p, &year, &ndigits)) /* year following the month */
78  return 0;
79  to = DATETIME_MONTH;
80  if (is_bc(&p))
81  bc = 1;
82  goto set;
83  }
84 
85  bc = is_bc(&p);
86  if (bc || !get_word(&p, word)) { /* just a year */
87  year = n;
88  to = DATETIME_YEAR;
89  goto set;
90  }
91  to = DATETIME_DAY; /* must be at least: day Mon year [bc] */
92  day = n;
93  if (!which_month(word, &month))
94  return 0;
95  if (!get_int(&p, &year, &ndigits))
96  return 0;
97  if (is_bc(&p))
98  bc = 1;
99 
100  /* now for the time */
101  if (!get_int(&p, &hour, &ndigits))
102  goto set;
103  to = DATETIME_HOUR;
104  if (*p != ':')
105  goto set;
106  p++;
107  if (!get_int(&p, &minute, &ndigits))
108  return 0;
109  if (ndigits != 2)
110  return 0;
111  to = DATETIME_MINUTE;
112  if (*p != ':')
113  goto timezone;
114  p++;
115  if (!get_double(&p, &second, &ndigits, &fracsec))
116  return 0;
117  if (ndigits != 2)
118  return 0;
119  to = DATETIME_SECOND;
120 
121 timezone:
122  if (!get_word(&p, word))
123  goto set;
124  if (!scan_tz(word, &tz))
125  return 0;
126  have_tz = 1;
127 
128 set:
129  if (more(&p)) /* make sure there isn't anything else */
130  return 0;
131  if (datetime_set_type(dt, DATETIME_ABSOLUTE, DATETIME_YEAR, to, fracsec))
132  return 0;
133  for (n = DATETIME_YEAR; n <= to; n++) {
134  switch (n) {
135  case DATETIME_YEAR:
136  if (datetime_set_year(dt, year))
137  return 0;
138  break;
139  case DATETIME_MONTH:
140  if (datetime_set_month(dt, month))
141  return 0;
142  break;
143  case DATETIME_DAY:
144  if (datetime_set_day(dt, day))
145  return 0;
146  break;
147  case DATETIME_HOUR:
148  if (datetime_set_hour(dt, hour))
149  return 0;
150  break;
151  case DATETIME_MINUTE:
152  if (datetime_set_minute(dt, minute))
153  return 0;
154  break;
155  case DATETIME_SECOND:
156  if (datetime_set_second(dt, second))
157  return 0;
158  break;
159  }
160  }
161  if (bc)
163  if (have_tz && datetime_set_timezone(dt, tz))
164  return 0;
165 
166  return 1;
167 }
168 
169 static int scan_relative(DateTime *dt, const char *buf)
170 {
171  const char *p;
172  double x;
173  int ndigits, ndecimal;
174  int pos;
175  int neg = 0;
176  int year = 0, month = 0, day = 0, hour = 0, minute = 0, fracsec = 0;
177  double second = 0.0;
178  int from = DATETIME_SECOND + 1, to = DATETIME_YEAR - 1;
179 
180  p = buf;
181  neg = minus_sign(&p);
182  if (!more(&p))
183  return 0;
184 
185  while (relative_term(&p, &x, &ndigits, &ndecimal, &pos)) {
186  if (from > pos)
187  from = pos;
188  if (to < pos)
189  to = pos;
190 
191  if (pos != DATETIME_SECOND && ndecimal != 0)
192  return 0;
193 
194  switch (pos) {
195  case DATETIME_YEAR:
196  year = (int)x;
197  break;
198  case DATETIME_MONTH:
199  month = (int)x;
200  break;
201  case DATETIME_DAY:
202  day = (int)x;
203  ;
204  break;
205  case DATETIME_HOUR:
206  hour = (int)x;
207  break;
208  case DATETIME_MINUTE:
209  minute = (int)x;
210  break;
211  case DATETIME_SECOND:
212  second = x;
213  fracsec = ndecimal;
214  break;
215  }
216  }
217 
218  if (more(&p)) /* make sure there isn't anything else */
219  return 0;
220  if (datetime_set_type(dt, DATETIME_RELATIVE, from, to, fracsec))
221  return 0;
222  for (pos = from; pos <= to; pos++) {
223  switch (pos) {
224  case DATETIME_YEAR:
225  if (datetime_set_year(dt, year))
226  return 0;
227  break;
228  case DATETIME_MONTH:
229  if (datetime_set_month(dt, month))
230  return 0;
231  break;
232  case DATETIME_DAY:
233  if (datetime_set_day(dt, day))
234  return 0;
235  break;
236  case DATETIME_HOUR:
237  if (datetime_set_hour(dt, hour))
238  return 0;
239  break;
240  case DATETIME_MINUTE:
241  if (datetime_set_minute(dt, minute))
242  return 0;
243  break;
244  case DATETIME_SECOND:
245  if (datetime_set_second(dt, second))
246  return 0;
247  break;
248  }
249  }
250  if (neg)
252 
253  return 1;
254 }
255 
256 static int is_space(char c)
257 {
258  return (c == ' ' || c == '\t' || c == '\n');
259 }
260 
261 static int is_digit(char c)
262 {
263  return (c >= '0' && c <= '9');
264 }
265 
266 static void skip_space(const char **s)
267 {
268  while (is_space(**s))
269  (*s)++;
270 }
271 
272 static int get_int(const char **s, int *n, int *ndigits)
273 {
274  const char *p;
275 
276  *n = 0;
277  skip_space(s);
278  p = *s;
279  for (*ndigits = 0; is_digit(*p); (*ndigits)++) {
280  *n *= 10;
281  *n += *p - '0';
282  p++;
283  }
284  if (*ndigits > 0)
285  *s = p;
286  return (*ndigits > 0);
287 }
288 
289 static int get_double(const char **s, double *x,
290  int *ndigits, /* number of digits before decimal */
291  int *ndecimal)
292 { /* number of decimal places */
293  char buf[1024];
294  char *b;
295  const char *p;
296 
297  skip_space(s);
298 
299  p = *s;
300  *ndecimal = 0;
301  b = buf;
302 
303  for (*ndigits = 0; is_digit(*p); (*ndigits)++)
304  *b++ = *p++;
305  if (*p == '.') {
306  *b++ = *p++;
307  while (is_digit(*p)) {
308  *b++ = *p++;
309  (*ndecimal)++;
310  }
311  }
312  *b = 0;
313  if (sscanf(buf, "%lf", x) != 1)
314  return 0;
315  *s = p;
316  return 1;
317 }
318 
319 /* if pos is non-zero, *(p-1) must be legal */
320 /*
321  static int
322  is_wordend (pos, p)
323  int pos;
324  char *p;
325  {
326  int d1, d0;
327 
328  if ('\0'==(*p)) return (1);
329  if (is_space(*p)) return (1);
330  if (pos){
331  d0 = is_digit(*(p-1));
332  d1 = is_digit(*p);
333  return(d0 != d1);
334  }
335  return (0);
336 
337  }
338  */
339 
340 /* get a word (between white space) and convert to lowercase */
341 static int get_word(const char **s, char *word)
342 {
343  const char *p;
344  int any;
345 
346  skip_space(s);
347  p = *s;
348  for (any = 0; *p && !is_space(*p); any = 1)
349  *word++ = lowercase(*p++);
350  *word = 0;
351  *s = p;
352  return any;
353 }
354 
355 static char lowercase(char c)
356 {
357  if (c >= 'A' && c <= 'Z')
358  c += 'a' - 'A';
359  return c;
360 }
361 
362 static int which_month(const char *name, int *n)
363 {
364  int i;
365 
366  for (i = 0; i < 12; i++)
367  if (strcmp(name, month_names[i]) == 0) {
368  *n = i + 1;
369  return 1;
370  }
371  return 0;
372 }
373 
374 static int is_bc(const char **s)
375 {
376  const char *p;
377  char word[1024];
378 
379  p = *s;
380  if (!get_word(&p, word))
381  return 0;
382  if (strcmp("bc", word) != 0)
383  return 0;
384  *s = p;
385  return 1;
386 }
387 
388 static int scan_tz(const char *word, int *tz)
389 {
390  int neg = 0;
391 
392  if (word[0] == '+')
393  neg = 0;
394  else if (word[0] == '-')
395  neg = 1;
396  else
397  return 0;
398 
399  if (!is_digit(word[1]))
400  return 0;
401  if (!is_digit(word[2]))
402  return 0;
403  if (!is_digit(word[3]))
404  return 0;
405  if (!is_digit(word[4]))
406  return 0;
407 
408  *tz = (word[1] - '0') * 600 + (word[2] - '0') * 60 + (word[3] - '0') * 10 +
409  (word[4] - '0');
410  if (neg)
411  *tz = -(*tz);
412  return 1;
413 }
414 
415 /* returns
416  0 not a recognized term
417  1 valid term, but perhaps illegal value
418  */
419 static int relative_term(const char **s, double *x, int *ndigits, int *ndecimal,
420  int *pos)
421 {
422  char word[1024];
423  const char *p;
424 
425  p = *s;
426  if (!get_double(&p, x, ndigits, ndecimal) || !get_word(&p, word))
427  return 0;
428 
429  if (strcmp(word, "year") == 0 || strcmp(word, "years") == 0)
430  *pos = DATETIME_YEAR;
431  else if (strcmp(word, "month") == 0 || strcmp(word, "months") == 0 ||
432  strcmp(word, "mon") == 0)
433  *pos = DATETIME_MONTH;
434  else if (strcmp(word, "day") == 0 || strcmp(word, "days") == 0)
435  *pos = DATETIME_DAY;
436  else if (strcmp(word, "hour") == 0 || strcmp(word, "hours") == 0)
437  *pos = DATETIME_HOUR;
438  else if (strcmp(word, "minute") == 0 || strcmp(word, "minutes") == 0 ||
439  strcmp(word, "min") == 0)
440  *pos = DATETIME_MINUTE;
441  else if (strcmp(word, "second") == 0 || strcmp(word, "seconds") == 0 ||
442  strcmp(word, "sec") == 0)
443  *pos = DATETIME_SECOND;
444  else
445  return 0;
446  *s = p;
447  return 1;
448 }
449 
450 static int minus_sign(const char **s)
451 {
452  skip_space(s);
453  if (**s == '-') {
454  (*s)++;
455  return 1;
456  }
457  return 0;
458 }
459 
460 static int is_relative(const char *buf)
461 {
462  int n;
463  double x;
464  const char *p;
465 
466  p = buf;
467  (void)minus_sign(&p);
468  return relative_term(&p, &x, &n, &n, &n) != 0;
469 }
470 
471 static int more(const char **s)
472 {
473  skip_space(s);
474  return **s != 0;
475 }
#define DATETIME_ABSOLUTE
Definition: datetime.h:4
#define DATETIME_MONTH
Definition: datetime.h:11
#define DATETIME_DAY
Definition: datetime.h:12
#define DATETIME_HOUR
Definition: datetime.h:13
#define DATETIME_SECOND
Definition: datetime.h:15
#define DATETIME_MINUTE
Definition: datetime.h:14
#define DATETIME_RELATIVE
Definition: datetime.h:5
#define DATETIME_YEAR
Definition: datetime.h:10
int datetime_error(int code, char *msg)
record 'code' and 'msg' as error code/msg (in static variables) code==0 will clear the error (ie set ...
void datetime_set_negative(DateTime *dt)
Makes the DateTime negative. (B.C. for ABSOLUTE DateTimes)
Definition: sign.c:60
int datetime_set_day(DateTime *dt, int day)
if dt.mode = ABSOLUTE, then the dt.year, dt.month:
Definition: values.c:327
int datetime_set_month(DateTime *dt, int month)
if dt.mode = ABSOLUTE, this also sets dt.day = 0
Definition: values.c:277
int datetime_set_type(DateTime *dt, int mode, int from, int to, int fracsec)
Definition: datetime/type.c:36
int datetime_set_timezone(DateTime *dt, int minutes)
returns 0 on success
Definition: tz1.c:64
int datetime_set_hour(DateTime *dt, int hour)
returns 0 on success or negative value on error
Definition: values.c:367
int datetime_set_year(DateTime *dt, int year)
if dt.mode = ABSOLUTE, this also sets dt.day = 0
Definition: values.c:232
int datetime_set_second(DateTime *dt, double second)
returns 0 on success or negative value on error
Definition: values.c:447
int datetime_set_minute(DateTime *dt, int minute)
returns 0 on success or negative value on error
Definition: values.c:407
const char * name
Definition: named_colr.c:6
double b
Definition: r_raster.c:39
int datetime_scan(DateTime *dt, const char *buf)
Convert the ascii string into a DateTime. This determines the mode/from/to based on the string,...
Definition: scan.c:40
#define x