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