~akopytov/percona-xtrabackup/bug1166888-2.1

« back to all changes in this revision

Viewing changes to src/libarchive/tar/getdate.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • mto: (391.1.5 staging)
  • mto: This revision was merged to the branch mainline in revision 390.
  • Revision ID: akopytov@gmail.com-20120210200556-6kx41z8wwrqfucro
Rebase of the parallel compression patch on new trunk + post-review
fixes.

Implementation of parallel compression and streaming for XtraBackup.

This revision implements the following changes:

* InnoDB files are now streamed by the xtrabackup binary rather than
innobackupex. As a result, integrity is now verified by xtrabackup and
thus tar4ibd is no longer needed, so it was removed.

* xtrabackup binary now accepts the new '--stream' option which has
exactly the same semantics as the '--stream' option in
innobackupex: it tells xtrabackup to stream all files to the standard
output in the specified format rather than storing them locally.

* The xtrabackup binary can now do parallel compression using the
quicklz library. Two new options were added to xtrabackup to support
this feature:

- '--compress' tells xtrabackup to compress all output data, including
the transaction log file and meta data files, using the specified
compression algorithm. The only currently supported algorithm is
'quicklz'. The resulting files have the qpress archive format,
i.e. every *.qp file produced by xtrabackup is essentially a one-file
qpress archive and can be extracted and uncompressed by the qpress
file archiver (http://www.quicklz.com/).

- '--compress-threads' specifies the number of worker threads used by
xtrabackup for parallel data compression. This option defaults to 1.

Parallel compression ('--compress-threads') can be used together with
parallel file copying ('--parallel'). For example, '--parallel=4
--compress --compress-threads=2' will create 4 IO threads that will
read the data and pipe it to 2 compression threads.

* To support simultaneous compression and streaming, a new custom
streaming format called 'xbstream' was introduced to XtraBackup in
addition to the 'tar' format. That was required to overcome some
limitations of traditional archive formats such as 'tar', 'cpio' and
others that do not allow streaming dynamically generated files, for
example dynamically compressed files.  Other advantages of xbstream over
traditional streaming/archive formats include ability to stream multiple
files concurrently (so it is possible to use streaming in the xbstream
format together with the --parallel option) and more compact data
storage.

* To allow streaming and extracting files to/from the xbstream format
produced by xtrabackup, a new utility aptly called 'xbstream' was
added to the XtraBackup distribution. This utility has a tar-like
interface:

- with the '-x' option it extracts files from the stream read from its
standard input to the current directory unless specified otherwise
with the '-C' option.

- with the '-c' option it streams files specified on the command line
to its standard output.

The utility also tries to minimize its impact on the OS page cache by
using the appropriate posix_fadvise() calls when available.

Show diffs side-by-side

added added

removed removed

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