Coverage Report

Created: 2026-03-12 06:35

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