Coverage Report

Created: 2026-09-14 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmlibarchive/libarchive/archive_parse_date.c
Line
Count
Source
1
/*
2
 * This code is in the public domain and has no copyright.
3
 *
4
 * This is a plain C recursive-descent translation of an old
5
 * public-domain YACC grammar that has been used for parsing dates in
6
 * very many open-source projects.
7
 *
8
 * Since the original authors were generous enough to donate their
9
 * work to the public domain, I feel compelled to match their
10
 * generosity.
11
 *
12
 * Tim Kientzle, February 2009.
13
 */
14
15
/*
16
 * Header comment from original getdate.y:
17
 */
18
19
/*
20
**  Originally written by Steven M. Bellovin <smb@research.att.com> while
21
**  at the University of North Carolina at Chapel Hill.  Later tweaked by
22
**  a couple of people on Usenet.  Completely overhauled by Rich $alz
23
**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
24
**
25
**  This grammar has 10 shift/reduce conflicts.
26
**
27
**  This code is in the public domain and has no copyright.
28
*/
29
30
#ifndef CM_PARSE_DATE
31
#include "archive_platform.h"
32
#endif
33
34
#include <ctype.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <time.h>
39
40
#include "archive.h"
41
#include "archive_integer.h"
42
43
/* Basic time units. */
44
0
#define EPOCH   1970
45
0
#define MINUTE    (60L)
46
0
#define HOUR    (60L * MINUTE)
47
0
#define DAY   (24L * HOUR)
48
49
/* Daylight-savings mode:  on, off, or not yet known. */
50
enum DSTMODE { DSTon, DSToff, DSTmaybe };
51
/* Meridian:  am or pm. */
52
enum { tAM, tPM };
53
/* Token types returned by nexttoken() */
54
enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
55
       tUNUMBER, tZONE, tDST, tERROR };
56
struct token { int token; time_t value; };
57
58
/*
59
 * Parser state.
60
 */
61
struct gdstate {
62
  struct token *tokenp; /* Pointer to next token. */
63
  /* HaveXxxx counts how many of this kind of phrase we've seen;
64
   * it's a fatal error to have more than one time, zone, day,
65
   * or date phrase. */
66
  int HaveYear;
67
  int HaveMonth;
68
  int HaveDay;
69
  int HaveWeekDay; /* Day of week */
70
  int HaveTime; /* Hour/minute/second */
71
  int HaveZone; /* timezone and/or DST info */
72
  int HaveRel; /* time offset; we can have more than one */
73
  /* Absolute time values. */
74
  time_t  Timezone;  /* Seconds offset from GMT */
75
  time_t  Day;
76
  time_t  Hour;
77
  time_t  Minutes;
78
  time_t  Month;
79
  time_t  Seconds;
80
  time_t  Year;
81
  /* DST selection */
82
  enum DSTMODE  DSTmode;
83
  /* Day of week accounting, e.g., "3rd Tuesday" */
84
  time_t  DayOrdinal; /* "3" in "3rd Tuesday" */
85
  time_t  DayNumber; /* "Tuesday" in "3rd Tuesday" */
86
  /* Relative time values: hour/day/week offsets are measured in
87
   * seconds, month/year are counted in months. */
88
  time_t  RelMonth;
89
  time_t  RelSeconds;
90
};
91
92
/*
93
 * A series of functions that recognize certain common time phrases.
94
 * Each function returns 1 if it managed to make sense of some of the
95
 * tokens, zero otherwise.
96
 */
97
98
/*
99
 *  hour:minute or hour:minute:second with optional AM, PM, or numeric
100
 *  timezone offset
101
 */
102
static int
103
timephrase(struct gdstate *gds)
104
0
{
105
0
  if (gds->tokenp[0].token == tUNUMBER
106
0
      && gds->tokenp[1].token == ':'
107
0
      && gds->tokenp[2].token == tUNUMBER
108
0
      && gds->tokenp[3].token == ':'
109
0
      && gds->tokenp[4].token == tUNUMBER) {
110
    /* "12:14:18" or "22:08:07" */
111
0
    ++gds->HaveTime;
112
0
    gds->Hour = gds->tokenp[0].value;
113
0
    gds->Minutes = gds->tokenp[2].value;
114
0
    gds->Seconds = gds->tokenp[4].value;
115
0
    gds->tokenp += 5;
116
0
  }
117
0
  else if (gds->tokenp[0].token == tUNUMBER
118
0
      && gds->tokenp[1].token == ':'
119
0
      && gds->tokenp[2].token == tUNUMBER) {
120
    /* "12:14" or "22:08" */
121
0
    ++gds->HaveTime;
122
0
    gds->Hour = gds->tokenp[0].value;
123
0
    gds->Minutes = gds->tokenp[2].value;
124
0
    gds->Seconds = 0;
125
0
    gds->tokenp += 3;
126
0
  }
127
0
  else if (gds->tokenp[0].token == tUNUMBER
128
0
      && gds->tokenp[1].token == tAMPM) {
129
    /* "7" is a time if it's followed by "am" or "pm" */
130
0
    ++gds->HaveTime;
131
0
    gds->Hour = gds->tokenp[0].value;
132
0
    gds->Minutes = gds->Seconds = 0;
133
    /* We'll handle the AM/PM below. */
134
0
    gds->tokenp += 1;
135
0
  } else {
136
    /* We can't handle this. */
137
0
    return 0;
138
0
  }
139
140
0
  if (gds->tokenp[0].token == tAMPM) {
141
    /* "7:12pm", "12:20:13am" */
142
0
    if (gds->Hour == 12)
143
0
      gds->Hour = 0;
144
0
    if (gds->tokenp[0].value == tPM)
145
0
      gds->Hour += 12;
146
0
    gds->tokenp += 1;
147
0
  }
148
0
  if (gds->tokenp[0].token == '+'
149
0
      && gds->tokenp[1].token == tUNUMBER) {
150
    /* "7:14+0700" */
151
0
    gds->HaveZone++;
152
0
    gds->DSTmode = DSToff;
153
0
    gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
154
0
        + (gds->tokenp[1].value % 100) * MINUTE);
155
0
    gds->tokenp += 2;
156
0
  }
157
0
  if (gds->tokenp[0].token == '-'
158
0
      && gds->tokenp[1].token == tUNUMBER) {
159
    /* "19:14:12-0530" */
160
0
    gds->HaveZone++;
161
0
    gds->DSTmode = DSToff;
162
0
    gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
163
0
        + (gds->tokenp[1].value % 100) * MINUTE);
164
0
    gds->tokenp += 2;
165
0
  }
166
0
  return 1;
167
0
}
Unexecuted instantiation: cm_parse_date.c:timephrase
Unexecuted instantiation: archive_parse_date.c:timephrase
168
169
/*
170
 * Timezone name, possibly including DST.
171
 */
172
static int
173
zonephrase(struct gdstate *gds)
174
0
{
175
0
  if (gds->tokenp[0].token == tZONE
176
0
      && gds->tokenp[1].token == tDST) {
177
0
    gds->HaveZone++;
178
0
    gds->Timezone = gds->tokenp[0].value;
179
0
    gds->DSTmode = DSTon;
180
0
    gds->tokenp += 1;
181
0
    return 1;
182
0
  }
183
184
0
  if (gds->tokenp[0].token == tZONE) {
185
0
    gds->HaveZone++;
186
0
    gds->Timezone = gds->tokenp[0].value;
187
0
    gds->DSTmode = DSToff;
188
0
    gds->tokenp += 1;
189
0
    return 1;
190
0
  }
191
192
0
  if (gds->tokenp[0].token == tDAYZONE) {
193
0
    gds->HaveZone++;
194
0
    gds->Timezone = gds->tokenp[0].value;
195
0
    gds->DSTmode = DSTon;
196
0
    gds->tokenp += 1;
197
0
    return 1;
198
0
  }
199
0
  return 0;
200
0
}
Unexecuted instantiation: cm_parse_date.c:zonephrase
Unexecuted instantiation: archive_parse_date.c:zonephrase
201
202
/*
203
 * Year/month/day in various combinations.
204
 */
205
static int
206
datephrase(struct gdstate *gds)
207
0
{
208
0
  if (gds->tokenp[0].token == tUNUMBER
209
0
      && gds->tokenp[1].token == '/'
210
0
      && gds->tokenp[2].token == tUNUMBER
211
0
      && gds->tokenp[3].token == '/'
212
0
      && gds->tokenp[4].token == tUNUMBER) {
213
0
    gds->HaveYear++;
214
0
    gds->HaveMonth++;
215
0
    gds->HaveDay++;
216
0
    if (gds->tokenp[0].value >= 13) {
217
      /* First number is big:  2004/01/29, 99/02/17 */
218
0
      gds->Year = gds->tokenp[0].value;
219
0
      gds->Month = gds->tokenp[2].value;
220
0
      gds->Day = gds->tokenp[4].value;
221
0
    } else if ((gds->tokenp[4].value >= 13)
222
0
        || (gds->tokenp[2].value >= 13)) {
223
      /* Last number is big:  01/07/98 */
224
      /* Middle number is big:  01/29/04 */
225
0
      gds->Month = gds->tokenp[0].value;
226
0
      gds->Day = gds->tokenp[2].value;
227
0
      gds->Year = gds->tokenp[4].value;
228
0
    } else {
229
      /* No significant clues: 02/03/04 */
230
0
      gds->Month = gds->tokenp[0].value;
231
0
      gds->Day = gds->tokenp[2].value;
232
0
      gds->Year = gds->tokenp[4].value;
233
0
    }
234
0
    gds->tokenp += 5;
235
0
    return 1;
236
0
  }
237
238
0
  if (gds->tokenp[0].token == tUNUMBER
239
0
      && gds->tokenp[1].token == '/'
240
0
      && gds->tokenp[2].token == tUNUMBER) {
241
    /* "1/15" */
242
0
    gds->HaveMonth++;
243
0
    gds->HaveDay++;
244
0
    gds->Month = gds->tokenp[0].value;
245
0
    gds->Day = gds->tokenp[2].value;
246
0
    gds->tokenp += 3;
247
0
    return 1;
248
0
  }
249
250
0
  if (gds->tokenp[0].token == tUNUMBER
251
0
      && gds->tokenp[1].token == '-'
252
0
      && gds->tokenp[2].token == tUNUMBER
253
0
      && gds->tokenp[3].token == '-'
254
0
      && gds->tokenp[4].token == tUNUMBER) {
255
    /* ISO 8601 format.  yyyy-mm-dd.  */
256
0
    gds->HaveYear++;
257
0
    gds->HaveMonth++;
258
0
    gds->HaveDay++;
259
0
    gds->Year = gds->tokenp[0].value;
260
0
    gds->Month = gds->tokenp[2].value;
261
0
    gds->Day = gds->tokenp[4].value;
262
0
    gds->tokenp += 5;
263
0
    return 1;
264
0
  }
265
266
0
  if (gds->tokenp[0].token == tUNUMBER
267
0
      && gds->tokenp[1].token == '-'
268
0
      && gds->tokenp[2].token == tMONTH
269
0
      && gds->tokenp[3].token == '-'
270
0
      && gds->tokenp[4].token == tUNUMBER) {
271
0
    gds->HaveYear++;
272
0
    gds->HaveMonth++;
273
0
    gds->HaveDay++;
274
0
    if (gds->tokenp[0].value > 31) {
275
      /* e.g. 1992-Jun-17 */
276
0
      gds->Year = gds->tokenp[0].value;
277
0
      gds->Month = gds->tokenp[2].value;
278
0
      gds->Day = gds->tokenp[4].value;
279
0
    } else {
280
      /* e.g. 17-JUN-1992.  */
281
0
      gds->Day = gds->tokenp[0].value;
282
0
      gds->Month = gds->tokenp[2].value;
283
0
      gds->Year = gds->tokenp[4].value;
284
0
    }
285
0
    gds->tokenp += 5;
286
0
    return 1;
287
0
  }
288
289
0
  if (gds->tokenp[0].token == tMONTH
290
0
      && gds->tokenp[1].token == tUNUMBER
291
0
      && gds->tokenp[2].token == ','
292
0
      && gds->tokenp[3].token == tUNUMBER) {
293
    /* "June 17, 2001" */
294
0
    gds->HaveYear++;
295
0
    gds->HaveMonth++;
296
0
    gds->HaveDay++;
297
0
    gds->Month = gds->tokenp[0].value;
298
0
    gds->Day = gds->tokenp[1].value;
299
0
    gds->Year = gds->tokenp[3].value;
300
0
    gds->tokenp += 4;
301
0
    return 1;
302
0
  }
303
304
0
  if (gds->tokenp[0].token == tMONTH
305
0
      && gds->tokenp[1].token == tUNUMBER) {
306
    /* "May 3" */
307
0
    gds->HaveMonth++;
308
0
    gds->HaveDay++;
309
0
    gds->Month = gds->tokenp[0].value;
310
0
    gds->Day = gds->tokenp[1].value;
311
0
    gds->tokenp += 2;
312
0
    return 1;
313
0
  }
314
315
0
  if (gds->tokenp[0].token == tUNUMBER
316
0
      && gds->tokenp[1].token == tMONTH
317
0
      && gds->tokenp[2].token == tUNUMBER) {
318
    /* "12 Sept 1997" */
319
0
    gds->HaveYear++;
320
0
    gds->HaveMonth++;
321
0
    gds->HaveDay++;
322
0
    gds->Day = gds->tokenp[0].value;
323
0
    gds->Month = gds->tokenp[1].value;
324
0
    gds->Year = gds->tokenp[2].value;
325
0
    gds->tokenp += 3;
326
0
    return 1;
327
0
  }
328
329
0
  if (gds->tokenp[0].token == tUNUMBER
330
0
      && gds->tokenp[1].token == tMONTH) {
331
    /* "12 Sept" */
332
0
    gds->HaveMonth++;
333
0
    gds->HaveDay++;
334
0
    gds->Day = gds->tokenp[0].value;
335
0
    gds->Month = gds->tokenp[1].value;
336
0
    gds->tokenp += 2;
337
0
    return 1;
338
0
  }
339
340
0
  return 0;
341
0
}
Unexecuted instantiation: cm_parse_date.c:datephrase
Unexecuted instantiation: archive_parse_date.c:datephrase
342
343
/*
344
 * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
345
 */
346
static int
347
relunitphrase(struct gdstate *gds)
348
0
{
349
0
  if (gds->tokenp[0].token == '-'
350
0
      && gds->tokenp[1].token == tUNUMBER
351
0
      && gds->tokenp[2].token == tSEC_UNIT) {
352
    /* "-3 hours" */
353
0
    gds->HaveRel++;
354
0
    gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
355
0
    gds->tokenp += 3;
356
0
    return 1;
357
0
  }
358
0
  if (gds->tokenp[0].token == '+'
359
0
      && gds->tokenp[1].token == tUNUMBER
360
0
      && gds->tokenp[2].token == tSEC_UNIT) {
361
    /* "+1 minute" */
362
0
    gds->HaveRel++;
363
0
    gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
364
0
    gds->tokenp += 3;
365
0
    return 1;
366
0
  }
367
0
  if (gds->tokenp[0].token == tUNUMBER
368
0
      && gds->tokenp[1].token == tSEC_UNIT) {
369
    /* "1 day" */
370
0
    gds->HaveRel++;
371
0
    gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
372
0
    gds->tokenp += 2;
373
0
    return 1;
374
0
  }
375
0
  if (gds->tokenp[0].token == '-'
376
0
      && gds->tokenp[1].token == tUNUMBER
377
0
      && gds->tokenp[2].token == tMONTH_UNIT) {
378
    /* "-3 months" */
379
0
    gds->HaveRel++;
380
0
    gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
381
0
    gds->tokenp += 3;
382
0
    return 1;
383
0
  }
384
0
  if (gds->tokenp[0].token == '+'
385
0
      && gds->tokenp[1].token == tUNUMBER
386
0
      && gds->tokenp[2].token == tMONTH_UNIT) {
387
    /* "+5 years" */
388
0
    gds->HaveRel++;
389
0
    gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
390
0
    gds->tokenp += 3;
391
0
    return 1;
392
0
  }
393
0
  if (gds->tokenp[0].token == tUNUMBER
394
0
      && gds->tokenp[1].token == tMONTH_UNIT) {
395
    /* "2 years" */
396
0
    gds->HaveRel++;
397
0
    gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
398
0
    gds->tokenp += 2;
399
0
    return 1;
400
0
  }
401
0
  if (gds->tokenp[0].token == tSEC_UNIT) {
402
    /* "now", "tomorrow" */
403
0
    gds->HaveRel++;
404
0
    gds->RelSeconds += gds->tokenp[0].value;
405
0
    gds->tokenp += 1;
406
0
    return 1;
407
0
  }
408
0
  if (gds->tokenp[0].token == tMONTH_UNIT) {
409
    /* "month" */
410
0
    gds->HaveRel++;
411
0
    gds->RelMonth += gds->tokenp[0].value;
412
0
    gds->tokenp += 1;
413
0
    return 1;
414
0
  }
415
0
  return 0;
416
0
}
Unexecuted instantiation: cm_parse_date.c:relunitphrase
Unexecuted instantiation: archive_parse_date.c:relunitphrase
417
418
/*
419
 * Day of the week specification.
420
 */
421
static int
422
dayphrase(struct gdstate *gds)
423
0
{
424
0
  if (gds->tokenp[0].token == tDAY) {
425
    /* "tues", "wednesday," */
426
0
    gds->HaveWeekDay++;
427
0
    gds->DayOrdinal = 1;
428
0
    gds->DayNumber = gds->tokenp[0].value;
429
0
    gds->tokenp += 1;
430
0
    if (gds->tokenp[0].token == ',')
431
0
      gds->tokenp += 1;
432
0
    return 1;
433
0
  }
434
0
  if (gds->tokenp[0].token == tUNUMBER
435
0
    && gds->tokenp[1].token == tDAY) {
436
    /* "second tues" "3 wed" */
437
0
    gds->HaveWeekDay++;
438
0
    gds->DayOrdinal = gds->tokenp[0].value;
439
0
    gds->DayNumber = gds->tokenp[1].value;
440
0
    gds->tokenp += 2;
441
0
    return 1;
442
0
  }
443
0
  return 0;
444
0
}
Unexecuted instantiation: cm_parse_date.c:dayphrase
Unexecuted instantiation: archive_parse_date.c:dayphrase
445
446
/*
447
 * Try to match a phrase using one of the above functions.
448
 * This layer also deals with a couple of generic issues.
449
 */
450
static int
451
phrase(struct gdstate *gds)
452
0
{
453
0
  if (timephrase(gds))
454
0
    return 1;
455
0
  if (zonephrase(gds))
456
0
    return 1;
457
0
  if (datephrase(gds))
458
0
    return 1;
459
0
  if (dayphrase(gds))
460
0
    return 1;
461
0
  if (relunitphrase(gds)) {
462
0
    if (gds->tokenp[0].token == tAGO) {
463
0
      gds->RelSeconds = -gds->RelSeconds;
464
0
      gds->RelMonth = -gds->RelMonth;
465
0
      gds->tokenp += 1;
466
0
    }
467
0
    return 1;
468
0
  }
469
470
  /* Bare numbers sometimes have meaning. */
471
0
  if (gds->tokenp[0].token == tUNUMBER) {
472
0
    if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
473
0
      gds->HaveYear++;
474
0
      gds->Year = gds->tokenp[0].value;
475
0
      gds->tokenp += 1;
476
0
      return 1;
477
0
    }
478
479
0
    if(gds->tokenp[0].value > 10000) {
480
      /* "20040301" */
481
0
      gds->HaveYear++;
482
0
      gds->HaveMonth++;
483
0
      gds->HaveDay++;
484
0
      gds->Day= (gds->tokenp[0].value)%100;
485
0
      gds->Month= (gds->tokenp[0].value/100)%100;
486
0
      gds->Year = gds->tokenp[0].value/10000;
487
0
      gds->tokenp += 1;
488
0
      return 1;
489
0
    }
490
491
0
    if (gds->tokenp[0].value < 24) {
492
0
      gds->HaveTime++;
493
0
      gds->Hour = gds->tokenp[0].value;
494
0
      gds->Minutes = 0;
495
0
      gds->Seconds = 0;
496
0
      gds->tokenp += 1;
497
0
      return 1;
498
0
    }
499
500
0
    if ((gds->tokenp[0].value / 100 < 24)
501
0
        && (gds->tokenp[0].value % 100 < 60)) {
502
      /* "513" is same as "5:13" */
503
0
      gds->Hour = gds->tokenp[0].value / 100;
504
0
      gds->Minutes = gds->tokenp[0].value % 100;
505
0
      gds->Seconds = 0;
506
0
      gds->tokenp += 1;
507
0
      return 1;
508
0
    }
509
0
  }
510
511
0
  return 0;
512
0
}
Unexecuted instantiation: cm_parse_date.c:phrase
Unexecuted instantiation: archive_parse_date.c:phrase
513
514
/*
515
 * A dictionary of time words.
516
 */
517
static struct LEXICON {
518
  size_t    abbrev;
519
  const char  *name;
520
  int   type;
521
  time_t    value;
522
} const TimeWords[] = {
523
  /* am/pm */
524
  { 0, "am",    tAMPM,  tAM },
525
  { 0, "pm",    tAMPM,  tPM },
526
527
  /* Month names. */
528
  { 3, "january",   tMONTH,  1 },
529
  { 3, "february",  tMONTH,  2 },
530
  { 3, "march",   tMONTH,  3 },
531
  { 3, "april",   tMONTH,  4 },
532
  { 3, "may",   tMONTH,  5 },
533
  { 3, "june",    tMONTH,  6 },
534
  { 3, "july",    tMONTH,  7 },
535
  { 3, "august",    tMONTH,  8 },
536
  { 3, "september", tMONTH,  9 },
537
  { 3, "october",   tMONTH, 10 },
538
  { 3, "november",  tMONTH, 11 },
539
  { 3, "december",  tMONTH, 12 },
540
541
  /* Days of the week. */
542
  { 2, "sunday",    tDAY, 0 },
543
  { 3, "monday",    tDAY, 1 },
544
  { 2, "tuesday",   tDAY, 2 },
545
  { 3, "wednesday", tDAY, 3 },
546
  { 2, "thursday",  tDAY, 4 },
547
  { 2, "friday",    tDAY, 5 },
548
  { 2, "saturday",  tDAY, 6 },
549
550
  /* Timezones: Offsets are in seconds. */
551
  { 0, "gmt",  tZONE,     0*HOUR }, /* Greenwich Mean */
552
  { 0, "ut",   tZONE,     0*HOUR }, /* Universal (Coordinated) */
553
  { 0, "utc",  tZONE,     0*HOUR },
554
  { 0, "wet",  tZONE,     0*HOUR }, /* Western European */
555
  { 0, "bst",  tDAYZONE,  0*HOUR }, /* British Summer */
556
  { 0, "wat",  tZONE,     1*HOUR }, /* West Africa */
557
  { 0, "at",   tZONE,     2*HOUR }, /* Azores */
558
  /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
559
  /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
560
  { 0, "nft",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland */
561
  { 0, "nst",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland Standard */
562
  { 0, "ndt",  tDAYZONE,  3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
563
  { 0, "ast",  tZONE,     4*HOUR }, /* Atlantic Standard */
564
  { 0, "adt",  tDAYZONE,  4*HOUR }, /* Atlantic Daylight */
565
  { 0, "est",  tZONE,     5*HOUR }, /* Eastern Standard */
566
  { 0, "edt",  tDAYZONE,  5*HOUR }, /* Eastern Daylight */
567
  { 0, "cst",  tZONE,     6*HOUR }, /* Central Standard */
568
  { 0, "cdt",  tDAYZONE,  6*HOUR }, /* Central Daylight */
569
  { 0, "mst",  tZONE,     7*HOUR }, /* Mountain Standard */
570
  { 0, "mdt",  tDAYZONE,  7*HOUR }, /* Mountain Daylight */
571
  { 0, "pst",  tZONE,     8*HOUR }, /* Pacific Standard */
572
  { 0, "pdt",  tDAYZONE,  8*HOUR }, /* Pacific Daylight */
573
  { 0, "yst",  tZONE,     9*HOUR }, /* Yukon Standard */
574
  { 0, "ydt",  tDAYZONE,  9*HOUR }, /* Yukon Daylight */
575
  { 0, "hst",  tZONE,     10*HOUR }, /* Hawaii Standard */
576
  { 0, "hdt",  tDAYZONE,  10*HOUR }, /* Hawaii Daylight */
577
  { 0, "cat",  tZONE,     10*HOUR }, /* Central Alaska */
578
  { 0, "ahst", tZONE,     10*HOUR }, /* Alaska-Hawaii Standard */
579
  { 0, "nt",   tZONE,     11*HOUR }, /* Nome */
580
  { 0, "idlw", tZONE,     12*HOUR }, /* Intl Date Line West */
581
  { 0, "cet",  tZONE,     -1*HOUR }, /* Central European */
582
  { 0, "met",  tZONE,     -1*HOUR }, /* Middle European */
583
  { 0, "mewt", tZONE,     -1*HOUR }, /* Middle European Winter */
584
  { 0, "mest", tDAYZONE,  -1*HOUR }, /* Middle European Summer */
585
  { 0, "swt",  tZONE,     -1*HOUR }, /* Swedish Winter */
586
  { 0, "sst",  tDAYZONE,  -1*HOUR }, /* Swedish Summer */
587
  { 0, "fwt",  tZONE,     -1*HOUR }, /* French Winter */
588
  { 0, "fst",  tDAYZONE,  -1*HOUR }, /* French Summer */
589
  { 0, "eet",  tZONE,     -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
590
  { 0, "bt",   tZONE,     -3*HOUR }, /* Baghdad, USSR Zone 2 */
591
  { 0, "it",   tZONE,     -3*HOUR-30*MINUTE },/* Iran */
592
  { 0, "zp4",  tZONE,     -4*HOUR }, /* USSR Zone 3 */
593
  { 0, "zp5",  tZONE,     -5*HOUR }, /* USSR Zone 4 */
594
  { 0, "ist",  tZONE,     -5*HOUR-30*MINUTE },/* Indian Standard */
595
  { 0, "zp6",  tZONE,     -6*HOUR }, /* USSR Zone 5 */
596
  /* { 0, "nst",  tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
597
  /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
598
  { 0, "wast", tZONE,     -7*HOUR }, /* West Australian Standard */
599
  { 0, "wadt", tDAYZONE,  -7*HOUR }, /* West Australian Daylight */
600
  { 0, "jt",   tZONE,     -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
601
  { 0, "cct",  tZONE,     -8*HOUR }, /* China Coast, USSR Zone 7 */
602
  { 0, "jst",  tZONE,     -9*HOUR }, /* Japan Std, USSR Zone 8 */
603
  { 0, "cast", tZONE,     -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
604
  { 0, "cadt", tDAYZONE,  -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
605
  { 0, "east", tZONE,     -10*HOUR }, /* Eastern Australian Std */
606
  { 0, "eadt", tDAYZONE,  -10*HOUR }, /* Eastern Australian Daylt */
607
  { 0, "gst",  tZONE,     -10*HOUR }, /* Guam Std, USSR Zone 9 */
608
  { 0, "nzt",  tZONE,     -12*HOUR }, /* New Zealand */
609
  { 0, "nzst", tZONE,     -12*HOUR }, /* New Zealand Standard */
610
  { 0, "nzdt", tDAYZONE,  -12*HOUR }, /* New Zealand Daylight */
611
  { 0, "idle", tZONE,     -12*HOUR }, /* Intl Date Line East */
612
613
  { 0, "dst",  tDST,    0 },
614
615
  /* Time units. */
616
  { 4, "years",   tMONTH_UNIT,  12 },
617
  { 5, "months",    tMONTH_UNIT,  1 },
618
  { 9, "fortnights",  tSEC_UNIT,  14 * DAY },
619
  { 4, "weeks",   tSEC_UNIT,  7 * DAY },
620
  { 3, "days",    tSEC_UNIT,  DAY },
621
  { 4, "hours",   tSEC_UNIT,  HOUR },
622
  { 3, "minutes",   tSEC_UNIT,  MINUTE },
623
  { 3, "seconds",   tSEC_UNIT,  1 },
624
625
  /* Relative-time words. */
626
  { 0, "tomorrow",  tSEC_UNIT,  DAY },
627
  { 0, "yesterday", tSEC_UNIT,  -DAY },
628
  { 0, "today",   tSEC_UNIT,  0 },
629
  { 0, "now",   tSEC_UNIT,  0 },
630
  { 0, "last",    tUNUMBER, -1 },
631
  { 0, "this",    tSEC_UNIT,  0 },
632
  { 0, "next",    tUNUMBER, 2 },
633
  { 0, "first",   tUNUMBER, 1 },
634
  { 0, "1st",   tUNUMBER, 1 },
635
/*  { 0, "second",    tUNUMBER, 2 }, */
636
  { 0, "2nd",   tUNUMBER, 2 },
637
  { 0, "third",   tUNUMBER, 3 },
638
  { 0, "3rd",   tUNUMBER, 3 },
639
  { 0, "fourth",    tUNUMBER, 4 },
640
  { 0, "4th",   tUNUMBER, 4 },
641
  { 0, "fifth",   tUNUMBER, 5 },
642
  { 0, "5th",   tUNUMBER, 5 },
643
  { 0, "sixth",   tUNUMBER, 6 },
644
  { 0, "seventh",   tUNUMBER, 7 },
645
  { 0, "eighth",    tUNUMBER, 8 },
646
  { 0, "ninth",   tUNUMBER, 9 },
647
  { 0, "tenth",   tUNUMBER, 10 },
648
  { 0, "eleventh",  tUNUMBER, 11 },
649
  { 0, "twelfth",   tUNUMBER, 12 },
650
  { 0, "ago",   tAGO,   1 },
651
652
  /* Military timezones. */
653
  { 0, "a", tZONE,  1*HOUR },
654
  { 0, "b", tZONE,  2*HOUR },
655
  { 0, "c", tZONE,  3*HOUR },
656
  { 0, "d", tZONE,  4*HOUR },
657
  { 0, "e", tZONE,  5*HOUR },
658
  { 0, "f", tZONE,  6*HOUR },
659
  { 0, "g", tZONE,  7*HOUR },
660
  { 0, "h", tZONE,  8*HOUR },
661
  { 0, "i", tZONE,  9*HOUR },
662
  { 0, "k", tZONE,  10*HOUR },
663
  { 0, "l", tZONE,  11*HOUR },
664
  { 0, "m", tZONE,  12*HOUR },
665
  { 0, "n", tZONE,  -1*HOUR },
666
  { 0, "o", tZONE,  -2*HOUR },
667
  { 0, "p", tZONE,  -3*HOUR },
668
  { 0, "q", tZONE,  -4*HOUR },
669
  { 0, "r", tZONE,  -5*HOUR },
670
  { 0, "s", tZONE,  -6*HOUR },
671
  { 0, "t", tZONE,  -7*HOUR },
672
  { 0, "u", tZONE,  -8*HOUR },
673
  { 0, "v", tZONE,  -9*HOUR },
674
  { 0, "w", tZONE,  -10*HOUR },
675
  { 0, "x", tZONE,  -11*HOUR },
676
  { 0, "y", tZONE,  -12*HOUR },
677
  { 0, "z", tZONE,  0*HOUR },
678
679
  /* End of table. */
680
  { 0, NULL,  0,  0 }
681
};
682
683
/*
684
 * Year is either:
685
 *  = A number from 0 to 99, which means a year from 1970 to 2069, or
686
 *  = The actual year (>=100).
687
 */
688
static time_t
689
Convert(time_t Month, time_t Day, time_t Year,
690
  time_t Hours, time_t Minutes, time_t Seconds,
691
  time_t Timezone, enum DSTMODE DSTmode)
692
0
{
693
0
  signed char DaysInMonth[12] = {
694
0
    31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
695
0
  };
696
0
  time_t    Julian;
697
0
  int   i;
698
0
  struct tm *ltime;
699
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
700
  struct tm tmbuf;
701
#endif
702
703
0
  if (Year < 69)
704
0
    Year += 2000;
705
0
  else if (Year < 100)
706
0
    Year += 1900;
707
0
  DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
708
0
      ? 29 : 28;
709
0
  if (Year < EPOCH || (sizeof(time_t) <= 4 && Year >= 2038)
710
0
      || Month < 1 || Month > 12
711
      /* Lint fluff:  "conversion from long may lose accuracy" */
712
0
      || Day < 1 || Day > DaysInMonth[(int)--Month]
713
0
      || Hours < 0 || Hours > 23
714
0
      || Minutes < 0 || Minutes > 59
715
0
      || Seconds < 0 || Seconds > 59)
716
0
    return -1;
717
718
0
  Julian = Day - 1;
719
0
  for (i = 0; i < Month; i++)
720
0
    Julian += DaysInMonth[i];
721
0
  for (i = EPOCH; i < Year; i++)
722
0
    Julian += 365 + (i % 4 == 0);
723
0
  Julian *= DAY;
724
0
  Julian += Timezone;
725
0
  Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
726
#if defined(HAVE_LOCALTIME_S)
727
  ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
728
#elif defined(HAVE_LOCALTIME_R)
729
  ltime = localtime_r(&Julian, &tmbuf);
730
#else
731
  ltime = localtime(&Julian);
732
#endif
733
0
  if (DSTmode == DSTon
734
0
      || (DSTmode == DSTmaybe && ltime->tm_isdst))
735
0
    Julian -= HOUR;
736
0
  return Julian;
737
0
}
Unexecuted instantiation: cm_parse_date.c:Convert
Unexecuted instantiation: archive_parse_date.c:Convert
738
739
static time_t
740
DSTcorrect(time_t Start, time_t Future)
741
0
{
742
0
  time_t    StartDay;
743
0
  time_t    FutureDay;
744
0
  struct tm *ltime;
745
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
746
  struct tm tmbuf;
747
#endif
748
#if defined(HAVE_LOCALTIME_S)
749
  ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
750
#elif defined(HAVE_LOCALTIME_R)
751
  ltime = localtime_r(&Start, &tmbuf);
752
#else
753
  ltime = localtime(&Start);
754
#endif
755
0
  StartDay = (ltime->tm_hour + 1) % 24;
756
#if defined(HAVE_LOCALTIME_S)
757
  ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
758
#elif defined(HAVE_LOCALTIME_R)
759
  ltime = localtime_r(&Future, &tmbuf);
760
#else
761
  ltime = localtime(&Future);
762
#endif
763
0
  FutureDay = (ltime->tm_hour + 1) % 24;
764
0
  return (Future - Start) + (StartDay - FutureDay) * HOUR;
765
0
}
Unexecuted instantiation: cm_parse_date.c:DSTcorrect
Unexecuted instantiation: archive_parse_date.c:DSTcorrect
766
767
768
static time_t
769
RelativeDate(time_t Start, time_t zone, int dstmode,
770
    time_t DayOrdinal, time_t DayNumber)
771
0
{
772
0
  struct tm *tm;
773
0
  time_t  t, now;
774
#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
775
  struct tm tmbuf;
776
#endif
777
778
0
  t = Start - zone;
779
#if defined(HAVE_GMTIME_S)
780
  tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
781
#elif defined(HAVE_GMTIME_R)
782
  tm = gmtime_r(&t, &tmbuf);
783
#else
784
  tm = gmtime(&t);
785
#endif
786
0
  now = Start;
787
0
  now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
788
0
  now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
789
0
  if (dstmode == DSTmaybe)
790
0
    return DSTcorrect(Start, now);
791
0
  return now - Start;
792
0
}
Unexecuted instantiation: cm_parse_date.c:RelativeDate
Unexecuted instantiation: archive_parse_date.c:RelativeDate
793
794
795
static time_t
796
RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
797
0
{
798
0
  struct tm *tm;
799
0
  time_t  Month;
800
0
  time_t  Year;
801
#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
802
  struct tm tmbuf;
803
#endif
804
805
0
  if (RelMonth == 0)
806
0
    return 0;
807
#if defined(HAVE_LOCALTIME_S)
808
  tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
809
#elif defined(HAVE_LOCALTIME_R)
810
0
  tm = localtime_r(&Start, &tmbuf);
811
#else
812
0
  tm = localtime(&Start);
813
0
#endif
814
0
  Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
815
0
  Year = Month / 12;
816
0
  Month = Month % 12 + 1;
817
0
  return DSTcorrect(Start,
818
0
      Convert(Month, (time_t)tm->tm_mday, Year,
819
0
    (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
820
0
    Timezone, DSTmaybe));
821
0
}
Unexecuted instantiation: cm_parse_date.c:RelativeMonth
Unexecuted instantiation: archive_parse_date.c:RelativeMonth
822
823
/*
824
 * Parses and consumes an unsigned 64-bit number.
825
 * Returns UINT64_MAX if the number overflows.
826
 */
827
static uint64_t
828
0
consume_unsigned_number(const char **in) {
829
0
  uint64_t value = 0;
830
0
  unsigned char c;
831
832
  /* Get the first character, abort if it's not a digit */
833
0
  c = (unsigned char)(**in);
834
0
  if (c < '0' || c > '9') {
835
0
    return UINT64_MAX;
836
0
  }
837
838
  /* Fold digits into the value, abort on overflow */
839
0
  while (c >= '0' && c <= '9') {
840
0
    unsigned char digit = c - '0';
841
842
    /* Return error if the result would overflow UINT64_MAX */
843
0
    if (archive_ckd_mul_u64(&value, value, 10) ||
844
0
        archive_ckd_add_u64(&value, value, digit)) {
845
0
      return UINT64_MAX;
846
0
    }
847
0
    (*in)++;
848
0
    c = (unsigned char)(**in);
849
0
  }
850
0
  return value;
851
0
}
Unexecuted instantiation: cm_parse_date.c:consume_unsigned_number
Unexecuted instantiation: archive_parse_date.c:consume_unsigned_number
852
853
/*
854
 * Tokenizer.
855
 */
856
static int
857
nexttoken(const char **in, time_t *value)
858
0
{
859
0
  char  c;
860
0
  char  buff[64];
861
862
0
  for ( ; ; ) {
863
0
    while (isspace((unsigned char)**in))
864
0
      ++*in;
865
866
    /* Skip parenthesized comments. */
867
0
    if (**in == '(') {
868
0
      int Count = 0;
869
0
      do {
870
0
        c = *(*in)++;
871
0
        if (c == '\0')
872
0
          return c;
873
0
        if (c == '(')
874
0
          Count++;
875
0
        else if (c == ')')
876
0
          Count--;
877
0
      } while (Count > 0);
878
0
      continue;
879
0
    }
880
881
    /* Try the next token in the word table first. */
882
    /* This allows us to match "2nd", for example. */
883
0
    {
884
0
      const char *src = *in;
885
0
      const struct LEXICON *tp;
886
0
      unsigned i = 0;
887
888
      /* Force to lowercase and strip '.' characters. */
889
0
      while (*src != '\0'
890
0
          && (isalnum((unsigned char)*src) || *src == '.')
891
0
          && i < sizeof(buff)-1) {
892
0
        if (*src != '.') {
893
0
          if (isupper((unsigned char)*src))
894
0
            buff[i++] = (char)tolower(
895
0
                (unsigned char)*src);
896
0
          else
897
0
            buff[i++] = *src;
898
0
        }
899
0
        src++;
900
0
      }
901
0
      buff[i] = '\0';
902
903
      /*
904
       * Find the first match.  If the word can be
905
       * abbreviated, make sure we match at least
906
       * the minimum abbreviation.
907
       */
908
0
      for (tp = TimeWords; tp->name; tp++) {
909
0
        size_t abbrev = tp->abbrev;
910
0
        if (abbrev == 0)
911
0
          abbrev = strlen(tp->name);
912
0
        if (strlen(buff) >= abbrev
913
0
            && strncmp(tp->name, buff, strlen(buff))
914
0
              == 0) {
915
          /* Skip over token. */
916
0
          *in = src;
917
          /* Return the match. */
918
0
          *value = tp->value;
919
0
          return tp->type;
920
0
        }
921
0
      }
922
0
    }
923
924
    /*
925
     * Not in the word table. If it starts with a digit,
926
     * it must be a number. Note: Because '-' and '+' have
927
     * other special meanings, I don't deal with signed
928
     * numbers here.
929
     */
930
0
    if (isdigit((unsigned char)(**in))) {
931
0
      uint64_t val = consume_unsigned_number(in);
932
0
      if (val > 9999) {
933
0
        return (tERROR);
934
0
      } else {
935
0
        *value = val;
936
0
        return (tUNUMBER);
937
0
      }
938
0
    }
939
940
0
    return *(*in)++;
941
0
  }
942
0
}
Unexecuted instantiation: cm_parse_date.c:nexttoken
Unexecuted instantiation: archive_parse_date.c:nexttoken
943
944
0
#define TM_YEAR_ORIGIN 1900
945
946
/* Yield A - B, measured in seconds.  */
947
static long
948
difftm (struct tm *a, struct tm *b)
949
0
{
950
0
  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
951
0
  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
952
0
  long days = (
953
    /* difference in day of year */
954
0
    a->tm_yday - b->tm_yday
955
    /* + intervening leap days */
956
0
    +  ((ay >> 2) - (by >> 2))
957
0
    -  (ay/100 - by/100)
958
0
    +  ((ay/100 >> 2) - (by/100 >> 2))
959
    /* + difference in years * 365 */
960
0
    +  (long)(ay-by) * 365
961
0
    );
962
0
  return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
963
0
      + (a->tm_min - b->tm_min) * MINUTE
964
0
      + (a->tm_sec - b->tm_sec));
965
0
}
Unexecuted instantiation: cm_parse_date.c:difftm
Unexecuted instantiation: archive_parse_date.c:difftm
966
967
/*
968
 * Parses a Unix epoch timestamp (seconds).
969
 * This supports a subset of what GNU tar accepts from black box testing,
970
 * but covers common use cases.
971
 */
972
static time_t
973
parse_unix_epoch(const char *p)
974
0
{
975
0
  uint64_t val;
976
0
  time_t epoch;
977
978
  /* may begin with + */
979
0
  if (*p == '+') {
980
0
    p++;
981
0
  }
982
983
  /* followed by some number */
984
0
  val = consume_unsigned_number(&p);
985
  /* Truncate to time_t */
986
0
  epoch = (time_t)val;
987
  /* If truncated value is different, then
988
   * the value is too large for `time_t`. */
989
0
  if (epoch < 0 || (uint64_t)epoch != val) {
990
0
    return (time_t)-1;
991
0
  }
992
  /* If there's any more characters, fail. */
993
0
  if (*p != '\0') {
994
0
    return (time_t)-1;
995
0
  }
996
997
0
  return epoch;
998
0
}
Unexecuted instantiation: cm_parse_date.c:parse_unix_epoch
Unexecuted instantiation: archive_parse_date.c:parse_unix_epoch
999
1000
/*
1001
 *
1002
 * The public function.
1003
 *
1004
 * TODO: tokens[] array should be dynamically sized.
1005
 */
1006
time_t
1007
archive_parse_date(time_t now, const char *p)
1008
0
{
1009
0
  struct token  tokens[256];
1010
0
  struct gdstate  _gds;
1011
0
  struct token  *lasttoken;
1012
0
  struct gdstate  *gds;
1013
0
  struct tm local, *tm;
1014
0
  struct tm gmt, *gmt_ptr;
1015
0
  time_t    Start;
1016
0
  time_t    tod;
1017
0
  long    tzone;
1018
1019
  /*
1020
   * @-prefixed Unix epoch timestamps (seconds)
1021
   * Skip the complex tokenizer - We do not want to accept strings like "@tenth"
1022
   */
1023
0
  if (*p == '@')
1024
0
    return parse_unix_epoch(p + 1);
1025
1026
  /* Clear out the parsed token array. */
1027
0
  memset(tokens, 0, sizeof(tokens));
1028
  /* Initialize the parser state. */
1029
0
  memset(&_gds, 0, sizeof(_gds));
1030
0
  gds = &_gds;
1031
1032
  /* Look up the current time. */
1033
#if defined(HAVE_LOCALTIME_S)
1034
  tm = localtime_s(&local, &now) ? NULL : &local;
1035
#elif defined(HAVE_LOCALTIME_R)
1036
  tm = localtime_r(&now, &local);
1037
#else
1038
  memset(&local, 0, sizeof(local));
1039
  tm = localtime(&now);
1040
#endif
1041
0
  if (tm == NULL)
1042
0
    return -1;
1043
#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
1044
0
  local = *tm;
1045
0
#endif
1046
1047
  /* Look up UTC if we can and use that to determine the current
1048
   * timezone offset. */
1049
#if defined(HAVE_GMTIME_S)
1050
  gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
1051
#elif defined(HAVE_GMTIME_R)
1052
0
  gmt_ptr = gmtime_r(&now, &gmt);
1053
#else
1054
  memset(&gmt, 0, sizeof(gmt));
1055
  gmt_ptr = gmtime(&now);
1056
0
  if (gmt_ptr != NULL) {
1057
    /* Copy, in case localtime and gmtime use the same buffer. */
1058
0
    gmt = *gmt_ptr;
1059
0
  }
1060
#endif
1061
0
  if (gmt_ptr != NULL)
1062
0
    tzone = difftm (&gmt, &local);
1063
0
  else
1064
    /* This system doesn't understand timezones; fake it. */
1065
0
    tzone = 0;
1066
0
  if(local.tm_isdst)
1067
0
    tzone += HOUR;
1068
1069
  /* Tokenize the input string. */
1070
0
  lasttoken = tokens;
1071
0
  while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
1072
0
    ++lasttoken;
1073
0
    if (lasttoken > tokens + 255)
1074
0
      return -1;
1075
0
  }
1076
0
  gds->tokenp = tokens;
1077
1078
  /* Match phrases until we run out of input tokens. */
1079
0
  while (gds->tokenp < lasttoken) {
1080
0
    if (!phrase(gds))
1081
0
      return -1;
1082
0
  }
1083
1084
  /* Use current local timezone if none was specified. */
1085
0
  if (!gds->HaveZone) {
1086
0
    gds->Timezone = tzone;
1087
0
    gds->DSTmode = DSTmaybe;
1088
0
  }
1089
1090
  /* If a timezone was specified, use that for generating the default
1091
   * time components instead of the local timezone. */
1092
0
  if (gds->HaveZone && gmt_ptr != NULL) {
1093
0
    now -= gds->Timezone;
1094
#if defined(HAVE_GMTIME_S)
1095
    gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
1096
#elif defined(HAVE_GMTIME_R)
1097
    gmt_ptr = gmtime_r(&now, &gmt);
1098
#else
1099
    gmt_ptr = gmtime(&now);
1100
#endif
1101
0
    if (gmt_ptr != NULL)
1102
0
      local = *gmt_ptr;
1103
0
    now += gds->Timezone;
1104
0
  }
1105
1106
0
  if (!gds->HaveYear)
1107
0
    gds->Year = local.tm_year + 1900;
1108
0
  if (!gds->HaveMonth)
1109
0
    gds->Month = local.tm_mon + 1;
1110
0
  if (!gds->HaveDay)
1111
0
    gds->Day = local.tm_mday;
1112
  /* Note: No default for hour/min/sec; a specifier that just
1113
   * gives date always refers to 00:00 on that date. */
1114
1115
  /* If we saw more than one time, timezone, weekday, year, month,
1116
   * or day, then give up. */
1117
0
  if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
1118
0
      || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
1119
0
    return -1;
1120
1121
  /* Compute an absolute time based on whatever absolute information
1122
   * we collected. */
1123
0
  if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
1124
0
      || gds->HaveTime || gds->HaveWeekDay) {
1125
0
    Start = Convert(gds->Month, gds->Day, gds->Year,
1126
0
        gds->Hour, gds->Minutes, gds->Seconds,
1127
0
        gds->Timezone, gds->DSTmode);
1128
0
    if (Start < 0)
1129
0
      return -1;
1130
0
  } else {
1131
0
    Start = now;
1132
0
    if (!gds->HaveRel)
1133
0
      Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
1134
0
          + local.tm_sec;
1135
0
  }
1136
1137
  /* Add the relative offset. */
1138
0
  Start += gds->RelSeconds;
1139
0
  Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
1140
1141
  /* Adjust for day-of-week offsets. */
1142
0
  if (gds->HaveWeekDay
1143
0
      && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
1144
0
    tod = RelativeDate(Start, gds->Timezone,
1145
0
        gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
1146
0
    Start += tod;
1147
0
  }
1148
1149
  /* -1 is an error indicator, so return 0 instead of -1 if
1150
   * that's the actual time. */
1151
0
  return Start == -1 ? 0 : Start;
1152
0
}
Unexecuted instantiation: cm_parse_date
Unexecuted instantiation: archive_parse_date
1153
1154
1155
#if defined(TEST)
1156
1157
/* ARGSUSED */
1158
int
1159
main(int argc, char **argv)
1160
{
1161
    time_t  d;
1162
    time_t  now = time(NULL);
1163
1164
    while (*++argv != NULL) {
1165
      (void)printf("Input: %s\n", *argv);
1166
      d = get_date(now, *argv);
1167
      if (d == -1)
1168
        (void)printf("Bad format - couldn't convert.\n");
1169
      else
1170
        (void)printf("Output: %s\n", ctime(&d));
1171
    }
1172
    exit(0);
1173
    /* NOTREACHED */
1174
}
1175
#endif  /* defined(TEST) */