In xvgr projects, set all patterns to 1.
[grace.git] / auxiliary / convcal.c
blobbf9a1476f9aa2dd98f1b052dbfbc880f580ee7a5
1 /*
2 * convcal : dates conversion utility
3 *
4 * Copyright (c) 1999 Luc Maisonobe
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * This programs allows you to convert dates between calendar format
25 * and numerical format.
27 * The following command will compile the program :
28 * cc -o convcal convcal.c -lm
32 #include <math.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <string.h>
37 #include <stdlib.h>
38 #ifndef EXIT_SUCCESS
39 #define EXIT_SUCCESS 0
40 #define EXIT_FAILURE 1
41 #endif
43 #define REFDATE "-4713-01-01T12:00:00"
45 typedef enum { FMT_iso,
46 FMT_european,
47 FMT_us,
48 FMT_days,
49 FMT_seconds,
50 FMT_nohint
51 } Dates_format;
53 typedef struct { int value;
54 int digits;
55 } Int_token;
59 * set of functions to convert julian calendar elements
60 * with negative years to julian day
62 static int neg_julian_non_leap (int year)
64 /* one leap year every four years, leap years : -4713, -4709, ..., -5, -1 */
65 return (3 - year) & 3;
68 static long neg_julian_cal_to_jul(int y, int m, int d)
70 /* day 0 : -4713-01-01
71 * day 1721423 : -1-12-31
73 return (1461L*(y + 1L))/4L
74 + (m*489)/16 - ((m > 2) ? (neg_julian_non_leap(y) ? 32L : 31L) : 30L)
75 + d + 1721057L;
79 static int neg_julian_year_estimate(long n)
81 /* year bounds : 4n - 6887153 <= 1461y <= 4n - 6885693
82 * lower bound reached 31st December of leap years
83 * upper bound reached 1st January of leap years
84 * the lower bound gives a low estimate of the year
86 return (int) ((4L*n - 6887153L)/1461L);
91 * set of functions to convert julian calendar elements
92 * with positive years to julian day
94 static int pos_julian_non_leap(int year)
96 /* one leap year every four years, leap years : 4, 8, ..., 1576, 1580 */
97 return year & 3;
100 static long pos_julian_cal_to_jul(int y, int m, int d)
102 /* day 1721424 : 1-01-01
103 * day 2299160 : 1582-10-04
105 return (1461L*(y -1L))/4L
106 + (m*489)/16 - ((m > 2) ? (pos_julian_non_leap(y) ? 32L : 31L) : 30L)
107 + d + 1721423L;
111 static int pos_julian_year_estimate(long n)
113 /* year bounds : 4n - 6885692 <= 1461y <= 4n - 6884232
114 * lower bound reached 31st December of leap years
115 * upper bound reached 1st January of leap years
116 * the lower bound gives a low estimate of the year
118 int y = (int) ((4L*n - 6885692L)/1461L);
120 /* make sure we stay in the positive model even with our underestimate */
121 return (y < 1) ? 1 : y;
127 * set of functions to convert gregorian calendar elements to julian day
129 static int gregorian_non_leap(int year)
131 /* one leap year every four years, except for multiple of 100 that
132 * are not also multiple of 400 (so 1600, 1896, 1904, and 2000 are
133 * leap years, but 1700, 1800 and 1900 are non leap years
135 return (year & 3) || ((year % 100) == 0 && ((year/100 & 3)));
138 static long gregorian_cal_to_jul(int y, int m, int d)
140 long c;
142 /* day 2299161 : 1582-10-15 */
143 c = (long) ((y - 1)/100);
144 return (1461L*(y - 1))/4 + c/4 - c
145 + (m*489)/16 - ((m > 2) ? (gregorian_non_leap(y) ? 32L : 31L) : 30L)
146 + d + 1721425L;
150 static int gregorian_year_estimate(long n)
153 * year bounds : 400n - 688570288 <= 146097y <= 400n - 688423712
154 * lower bound reached on : 1696-12-31, 2096-12-31, 2496-12-31 ...
155 * upper bound reached on : 1904-01-01, 2304-01-01, 2704-01-01 ...
156 * the lower bound gives a low estimate of the year
158 return (int) ((400L*n - 688570288L)/146097L);
163 * convert calendar elements to Julian day
165 long cal_to_jul(int y, int m, int d)
168 long n;
170 n = gregorian_cal_to_jul(y, m, d);
172 if (n < 2299161L) {
173 /* the date belongs to julian calendar */
174 n = (y < 0)
175 ? neg_julian_cal_to_jul(y, m, d)
176 : pos_julian_cal_to_jul(y, m, d);
179 return n;
185 * convert julian day to calendar elements
187 static void jul_to_some_cal(long n,
188 int (*some_non_leap) (int),
189 long (*some_cal_to_jul) (int, int, int),
190 int (*some_year_estimate) (long),
191 int *y, int *m, int *d)
193 int non_leap, day_of_year, days_until_end_of_year;
195 /* lower estimation of year */
196 *y = some_year_estimate(n);
197 non_leap = some_non_leap(*y);
198 days_until_end_of_year = (int) (some_cal_to_jul(*y, 12, 31) - n);
200 while (days_until_end_of_year < 0) {
201 /* correction of the estimate */
202 (*y)++;
203 non_leap = some_non_leap(*y);
204 days_until_end_of_year += non_leap ? 365 : 366;
207 day_of_year = (non_leap ? 365 : 366) - days_until_end_of_year;
209 /* estimate of the month : one too high only on last days of January */
210 *m = (16*(day_of_year + (non_leap ? 32 : 31))) / 489;
212 /* day of month */
213 *d = day_of_year
214 - (*m*489)/16 + ((*m > 2) ? (non_leap ? 32 : 31) : 30);
215 if (*d < 1) {
216 /* no luck, our estimate is false near end of January */
217 *m = 1;
218 *d += 31;
225 * convert julian day to calendar elements
227 void jul_to_cal(long n, int *y, int *m, int *d)
229 if (n < 1721424L) {
230 jul_to_some_cal(n, neg_julian_non_leap,
231 neg_julian_cal_to_jul, neg_julian_year_estimate,
232 y, m, d);
233 } else if (n < 2299161L) {
234 jul_to_some_cal(n, pos_julian_non_leap,
235 pos_julian_cal_to_jul, pos_julian_year_estimate,
236 y, m, d);
237 } else {
238 jul_to_some_cal(n, gregorian_non_leap,
239 gregorian_cal_to_jul, gregorian_year_estimate,
240 y, m, d);
246 * convert julian day and hourly elements to julian day
248 double jul_and_time_to_jul(long jul, int hour, int min, double sec)
250 return ((double) jul)
251 + (((double) (((hour - 12)*60 + min)*60)) + sec)/86400.0;
257 * convert calendar and hourly elements to julian day
259 double cal_and_time_to_jul(int y, int m, int d,
260 int hour, int min, double sec)
262 return jul_and_time_to_jul (cal_to_jul(y, m, d), hour, min, sec);
267 * convert julian day to calendar and hourly elements
268 * rounding_tol allows to say 1999-12-31T23:59:59.501
269 * should be rounded to 2000-01-01T00:00:00.000 assuming
270 * it is set to 0.5 second. It is wise to set it according
271 * to the display accuracy of seconds.
273 void jul_to_cal_and_time(double jday, double rounding_tol,
274 int *y, int *m, int *d,
275 int *hour, int *min, double *sec)
277 long n;
279 /* find the time of the day */
280 n = (long) floor(jday + 0.5);
281 *sec = 24.0*(jday + 0.5 - n);
282 *hour = (int) floor(*sec);
283 *sec = 60.0*(*sec - *hour);
284 *min = (int) floor(*sec);
285 *sec = 60.0*(*sec - *min);
286 if (*sec + rounding_tol >= 60.0) {
287 /* we should round to next minute */
288 *sec = 0.0;
289 *min += 1;
290 if (*min == 60) {
291 *min = 0;
292 *hour += 1;
293 if (*hour == 24) {
294 *hour = 0;
295 n++;
300 /* now find the date */
301 jul_to_cal(n, y, m, d);
306 * check the existence of given calendar elements
307 * this includes either number of day in the month
308 * and calendars pecularities (year 0 and October 1582)
310 static int check_date(int century, int wy,
311 Int_token y, Int_token m, Int_token d,
312 long *jul)
314 int y_expand, y_check, m_check, d_check;
316 /* expands years written with two digits only */
317 if (y.value >= 0 && y.value < wy && y.digits <= 2) {
318 y_expand = century + y.value;
319 } else if (y.value >= wy && y.value < 100 && y.digits <= 2) {
320 y_expand = century - 100 + y.value;
321 } else {
322 y_expand = y.value;
325 if (m.digits > 2 || d.digits > 2) {
326 /* this should be the year instead of either the month or the day */
327 return EXIT_FAILURE;
330 *jul = cal_to_jul(y_expand, m.value, d.value);
331 jul_to_cal(*jul, &y_check, &m_check, &d_check);
332 if (y_expand != y_check || m.value != m_check || d.value != d_check) {
333 return EXIT_FAILURE;
334 } else {
335 return EXIT_SUCCESS;
342 * lexical analyser for float data (knows about fortran exponent
343 * markers, return address of following data)
345 int parse_float(const char* s, double *value, const char **after)
347 int neg_mant, neg_exp, digits, dot_exp, raw_exp;
348 const char *after_dot;
350 /* we skip leading whitespace */
351 while (isspace(*s)) {
352 s++;
355 /* sign */
356 if (*s == '-') {
357 neg_mant = 1;
358 s++;
359 } else {
360 neg_mant = 0;
363 /* mantissa */
364 digits = 0;
365 *value = 0.0;
366 while (isdigit(*s)) {
367 *value = *value*10.0 + (*s++ - '0');
368 digits++;
370 if (*s == '.') {
371 after_dot = ++s;
372 while (isdigit(*s)) {
373 *value = *value*10.0 + (*s++ - '0');
374 digits++;
376 dot_exp = after_dot - s;
377 } else {
378 dot_exp = 0;
380 if (digits == 0) {
381 /* there should be at least one digit (either before or after dot) */
382 return EXIT_FAILURE;
385 /* exponent (d and D are fortran exponent markers) */
386 raw_exp = 0;
387 if (*s == 'e' || *s == 'E' || *s == 'd' || *s == 'D') {
388 s++;
389 if (*s == '-') {
390 neg_exp = 1;
391 s++;
392 } else {
393 neg_exp = 0;
394 if (*s == '+') {
395 s++;
398 while (isdigit(*s)) {
399 raw_exp = raw_exp*10 + (*s++ - '0');
401 if (neg_exp) {
402 raw_exp = -raw_exp;
406 /* read float */
407 *value = (neg_mant ? -(*value) : (*value)) * pow (10.0, dot_exp + raw_exp);
409 if (after != NULL) {
410 /* the caller wants to know what follows the float number */
411 *after = s;
414 return EXIT_SUCCESS;
420 * lexical analyser for calendar dates
421 * return the number of read elements, or -1 on failure
423 static int parse_calendar_date(const char* s,
424 Int_token tab [5], double *sec)
426 int i, waiting_separator, negative;
428 negative = 0;
429 waiting_separator = 0;
430 i = 0;
431 while (i < 5) {
432 /* loop from year to minute elements : all integers */
434 switch (*s) {
435 case '\0': /* end of string */
436 return i;
438 case ' ' : /* repeatable separator */
439 s++;
440 negative = 0;
441 break;
443 case '/' : case ':' : case '.' : case 'T' : /* non-repeatable separator */
444 if (waiting_separator) {
445 if ((*s == 'T') && (i != 3)) {
446 /* the T separator is only allowed between date
447 and time (mainly for iso8601) */
448 return -1;
450 s++;
451 negative = 0;
452 waiting_separator = 0;
453 } else {
454 return -1;
456 break;
458 case '-' : /* either separator or minus sign */
459 s++;
460 if (waiting_separator) {
461 negative = 0;
462 waiting_separator = 0;
463 } else if ((*s >= '0') && (*s <= '9')) {
464 negative = 1;
465 } else {
466 return -1;
468 break;
470 case '0' : case '1' : case '2' : case '3' : case '4' :
471 case '5' : case '6' : case '7' : case '8' : case '9' : /* digit */
472 tab[i].value = ((int) *s) - '0';
473 tab[i].digits = 1;
474 while (isdigit(*++s)) {
475 tab[i].value = tab[i].value*10 + (((int) *s) - '0');
476 tab[i].digits++;
478 if (negative) {
479 tab[i].value = -tab[i].value;
481 i++;
482 negative = 0;
483 waiting_separator = 1;
484 break;
486 default :
487 return -1;
493 while (isspace(*s)) {
494 s++;
496 if (*s == '\0') {
497 return 5;
500 if ((*s == '/') || (*s == ':') || (*s == '.') || (*s == '-')) {
501 /* this was the seconds separator */
502 s++;
504 /* seconds are read in float format */
505 if (parse_float(s, sec, &s) == EXIT_SUCCESS) {
506 while (isspace(*s)) {
507 s++;
509 if (*s == '\0') {
510 return 6;
516 /* something is wrong */
517 return -1;
523 * parse a date given either in calendar or numerical format
525 int parse_date(const char* s, int century, int wy, Dates_format preferred,
526 double *jul, Dates_format *recognized)
528 int i, n;
529 int ky, km, kd;
530 static Dates_format trials [] = {FMT_nohint, FMT_iso, FMT_european, FMT_us};
531 Int_token tab [5];
532 long j;
533 double sec;
534 const char *after;
536 /* first guess : is it a date in calendar format ? */
537 n = parse_calendar_date(s, tab, &sec);
538 switch (n) {
539 /* we consider hours, minutes and seconds as optional items */
540 case -1 : /* parse error */
541 break;
543 case 3 :
544 tab[3].value = 0; /* adding hours */
545 tab[3].digits = 1;
547 case 4 :
548 tab[4].value = 0; /* adding minutes */
549 tab[4].digits = 1;
551 case 5 :
552 sec = 0.0; /* adding seconds */
554 case 6 :
555 /* we now have a complete date */
557 /* try the user's choice first */
558 trials[0] = preferred;
560 for (i = 0; i < 4; i++) {
561 if (trials[i] == FMT_iso) {
562 /* YYYY-MM-DD */
563 ky = 0;
564 km = 1;
565 kd = 2;
566 } else if (trials[i] == FMT_european) {
567 /* DD/MM/(YY)YY */
568 ky = 2;
569 km = 1;
570 kd = 0;
571 } else if (trials[i] == FMT_us) {
572 /* MM/DD/(YY)YY */
573 ky = 2;
574 km = 0;
575 kd = 1;
576 } else {
577 /* the user didn't choose a calendar format */
578 continue;
581 if (check_date(century, wy, tab[ky], tab[km], tab[kd], &j)
582 == EXIT_SUCCESS) {
583 *jul = jul_and_time_to_jul(j, tab[3].value, tab[4].value,
584 sec);
585 *recognized = trials[i];
586 return EXIT_SUCCESS;
589 break;
591 default :
592 /* probably a julian date (integer if n == 1, real otherwise) */
593 break;
597 /* second guess : is it a date in numerical format ? */
598 if (parse_float(s, jul, &after) == EXIT_SUCCESS) {
599 while (isspace(*after)) {
600 after++;
602 if (*after == '\0') {
603 if (preferred == FMT_seconds) {
604 *recognized = FMT_seconds;
605 *jul /= 86400.0;
606 } else {
607 *recognized = FMT_days;
609 return EXIT_SUCCESS;
613 return EXIT_FAILURE;
617 int convert_and_write(const char *s,
618 int century, int wy, double reference_date,
619 Dates_format input_format, Dates_format output_format)
621 Dates_format recognized;
622 int y, m, d, hour, min;
623 double jul;
624 double sec;
626 if (parse_date(s, century, wy, input_format, &jul, &recognized)
627 != EXIT_SUCCESS) {
628 return EXIT_FAILURE;
631 if (recognized == FMT_days || recognized == FMT_seconds) {
632 /* the parsed value is relative to the reference date */
633 jul += reference_date;
636 if (output_format == FMT_nohint) {
637 /* choose a format that really convert calendar and numerical */
638 if ((recognized == FMT_days) || (recognized == FMT_seconds)) {
639 output_format = FMT_iso;
640 } else {
641 output_format = FMT_days;
645 switch (output_format) {
646 case FMT_iso :
647 jul_to_cal_and_time(jul, 0.0005, &y, &m, &d, &hour, &min, &sec);
648 fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%06.3f\n",
649 y, m, d, hour, min, sec);
650 break;
652 case FMT_european :
653 jul_to_cal_and_time(jul, 0.0005, &y, &m, &d, &hour, &min, &sec);
654 fprintf(stdout, "%02d/%02d/%04d %02d:%02d:%06.3f\n",
655 d, m, y, hour, min, sec);
656 break;
658 case FMT_us :
659 jul_to_cal_and_time(jul, 0.0005, &y, &m, &d, &hour, &min, &sec);
660 fprintf(stdout, "%02d/%02d/%04d %02d:%02d:%06.3f\n",
661 m, d, y, hour, min, sec);
662 break;
664 case FMT_days :
665 fprintf(stdout, "%17.8f\n", jul - reference_date);
666 break;
668 case FMT_seconds :
669 fprintf(stdout, "%17.3f\n", 86400.0 * (jul - reference_date));
670 break;
672 default :
673 fprintf(stderr, "%s:%d: internal error\n", __FILE__, __LINE__);
674 break;
678 return EXIT_SUCCESS;
682 int string_equal(const char *c1, const char *c2)
684 return (strlen(c1) == strlen(c2)) && (strcmp(c1, c2) == 0);
687 int parse_format(const char *s, Dates_format *f)
690 if (string_equal(s, "iso")) {
691 *f = FMT_iso;
692 } else if (string_equal(s, "european")) {
693 *f = FMT_european;
694 } else if (string_equal(s, "us")) {
695 *f = FMT_us;
696 } else if (string_equal(s, "days")) {
697 *f = FMT_days;
698 } else if (string_equal(s, "seconds")) {
699 *f = FMT_seconds;
700 } else if (string_equal(s, "nohint")) {
701 *f = FMT_nohint;
702 } else {
703 return EXIT_FAILURE;
706 return EXIT_SUCCESS;
711 * expand a line buffer
713 static void expand_line_buffer(char **adrBuf, int *ptrSize, char **adrPtr)
715 char *newbuf;
716 int newsize;
718 newsize = *ptrSize + 512;
719 newbuf = (char *) malloc(newsize);
720 if (newbuf == 0) {
721 fprintf(stderr, "Insufficient memory for line");
722 exit (EXIT_FAILURE);
725 if (*ptrSize == 0) {
726 /* this is the first time through */
727 if (adrPtr) {
728 *adrPtr = newbuf;
730 } else {
731 /* we are expanding an existing line */
732 strncpy(newbuf, *adrBuf, *ptrSize);
733 if (adrPtr) {
734 *adrPtr += newbuf - *adrBuf;
736 free(*adrBuf);
739 *adrBuf = newbuf;
740 *ptrSize = newsize;
745 * help message
747 static void usage (FILE *stream, const char *progname)
749 fprintf (stream,
750 "%s reads the dates either on the command line or in the\n", progname);
751 fprintf (stream,
752 "standard input if the command line contains no date. The following\n");
753 fprintf (stream,
754 "date formats are supported (hour, minutes and seconds are always optional):\n");
755 fprintf (stream,
756 "\n");
757 fprintf (stream,
758 "iso : 1999-12-31T23:59:59.999\n");
759 fprintf (stream,
760 "european : 31/12/1999 23:59:59.999 or 31/12/99 23:59:59.999\n");
761 fprintf (stream,
762 "us : 12/31/1999 23:59:59.999 or 12/31/99 23:59:59.999\n");
763 fprintf (stream,
764 "days : 123456.789\n");
765 fprintf (stream,
766 "seconds : 123456.789\n");
767 fprintf (stream,
768 "\n");
769 fprintf (stream,
770 "The formats are tried in the following order : users's choice,\n");
771 fprintf (stream,
772 "iso, european and us (there is no ambiguity between calendar\n");
773 fprintf (stream,
774 "formats and numerical formats and therefore no order is specified\n");
775 fprintf (stream,
776 "for them). The default user's choice (nohint) does nothing so the\n");
777 fprintf (stream,
778 "following formats of the list are used ; the main use of user's\n");
779 fprintf (stream,
780 "choice is to put another format before the other ones. The\n");
781 fprintf (stream,
782 "separators between various fields can be any characters in the set:\n");
783 fprintf (stream,
784 "\" :/.-T\". One or more spaces act as one separator, other characters\n");
785 fprintf (stream,
786 "can not be repeated, the T separator is allowed only between date and\n");
787 fprintf (stream,
788 "time, mainly for iso8601. So the string \"1999-12 31:23-59\" is allowed\n");
789 fprintf (stream,
790 "(but not recommended). The '-' character is used both as a\n");
791 fprintf (stream,
792 "separator (it is traditionally used in iso8601 format) and as the\n");
793 fprintf (stream,
794 "unary minus (for dates in the far past or for numerical\n");
795 fprintf (stream,
796 "dates). When the year is between 0 and 99 and is written with two\n");
797 fprintf (stream,
798 "or less digits, it is mapped to the era beginning at wrap year and\n");
799 fprintf (stream,
800 "ending at wrap year + 99 as follows :\n");
801 fprintf (stream,
802 " [wy ; 99] -> [ wrap_year ; 100*(1 + wrap_year/100) - 1 ]\n");
803 fprintf (stream,
804 " [00 ; wy-1] -> [ 100*(1 + wrap_year/100) ; wrap_year + 99]\n");
805 fprintf (stream,
806 "so for example if the wrap year is set to 1950 (which is the default\n");
807 fprintf (stream,
808 "value), then the mapping is :\n");
809 fprintf (stream,
810 " range [00 ; 49] is mapped to [2000 ; 2049]\n");
811 fprintf (stream,
812 " range [50 ; 99] is mapped to [1950 ; 1999]\n");
813 fprintf (stream,
814 "this is reasonably Y2K compliant and is consistent with current use.\n");
815 fprintf (stream,
816 "Specifying year 1 is still possible using more than two digits as\n");
817 fprintf (stream,
818 "follows : \"0001-03-04\" is unambiguously March the 4th, year 1, even\n");
819 fprintf (stream,
820 "if the user's choice is us format. However using two digits only is\n");
821 fprintf (stream,
822 "not recommended (we introduce a 2050 bug here so this feature\n");
823 fprintf (stream,
824 "should be removed at some point in the future ;-)\n");
825 fprintf (stream,
826 "\n");
827 fprintf (stream,
828 "Numerical dates (days and seconds formats) can be specified using\n");
829 fprintf (stream,
830 "integral, real or exponential formats (the 'd' and 'D' exponant\n");
831 fprintf (stream,
832 "markers from fortran are supported in addition to 'e' and 'E').\n");
833 fprintf (stream,
834 "They are computed according to a customizable reference date.\n");
835 fprintf (stream,
836 "The default value is given by the REFDATE constant in the source file.\n");
837 fprintf (stream,
838 "You can change this value as you want before compiling, and you can\n");
839 fprintf (stream,
840 "change it at will using the -r command line option. The default\n");
841 fprintf (stream,
842 "value in the distributed file is \"-4713-01-01T12:00:00\", it is a\n");
843 fprintf (stream,
844 "classical reference for astronomical events (note that the '-' is\n");
845 fprintf (stream,
846 "used here both as a unary minus and as a separator).\n");
847 fprintf (stream,
848 "\n");
849 fprintf (stream,
850 "The program can be used either for Denys's and gregorian\n");
851 fprintf (stream,
852 "calendars. It does not take into account leap seconds : you can\n");
853 fprintf (stream,
854 "think it works only in International Atomic Time (TAI) and not in\n");
855 fprintf (stream,
856 "Coordinated Unified Time (UTC) ... Inexistant dates are detected,\n");
857 fprintf (stream,
858 "they include year 0, dates between 1582-10-05 and 1582-10-14,\n");
859 fprintf (stream,
860 "February 29th of non leap years, months below 1 or above 12, ...\n");
861 fprintf (stream,
862 "\n");
863 fprintf (stream,
864 "The following command line options are supported. Apart from the -h\n");
865 fprintf (stream,
866 "flag, all of these options can be used several times, each new\n");
867 fprintf (stream,
868 "value overriding the preceding one.\n");
869 fprintf (stream,
870 "\n");
871 fprintf (stream,
872 "-i format : set user's choice for input format, supported formats are\n");
873 fprintf (stream,
874 " iso, european, us, days, seconds and nohint.\n");
875 fprintf (stream,
876 " At the beginning the input format is nohint, which means\n");
877 fprintf (stream,
878 " the program try to guess the format by itself, if the\n");
879 fprintf (stream,
880 " user's choice does not allow to parse the date, other\n");
881 fprintf (stream,
882 " formats are tried\n");
883 fprintf (stream,
884 "-o format : force output format, supported formats are\n");
885 fprintf (stream,
886 " iso, european, us, days, seconds and nohint.\n");
887 fprintf (stream,
888 " At the beginning, the output format is nohint, which means\n");
889 fprintf (stream,
890 " the program uses days format for dates read in any\n");
891 fprintf (stream,
892 " calendar format and uses iso8601 for dates read in\n");
893 fprintf (stream,
894 " numerical format\n");
895 fprintf (stream,
896 "-r date : set reference date (the date is read using the current\n");
897 fprintf (stream,
898 " input format) at the beginning the reference is set\n");
899 fprintf (stream,
900 " according to the REFDATE constant below.\n");
901 fprintf (stream,
902 "-w year : set the wrap year to year\n");
903 fprintf (stream,
904 "-h : prints this help message on stderr and exits successfully\n");
906 exit(0);
910 * driver program
912 int main(int argc, char *argv[])
914 double reference_date;
915 Dates_format input_format;
916 Dates_format output_format;
917 Dates_format recognized;
918 int century, wy;
920 int i, j, converted;
921 int retval = EXIT_SUCCESS;
923 /* initial values */
924 century = 2000;
925 wy = 50;
926 if (parse_date(REFDATE, century, wy, FMT_iso, &reference_date, &recognized)
927 != EXIT_SUCCESS) {
928 fprintf(stderr,
929 "%s: unable to parse compiled in reference date (%s) !\n",
930 argv[0], REFDATE);
931 return EXIT_FAILURE;
933 input_format = FMT_nohint;
934 output_format = FMT_nohint;
936 /* command line parsing */
937 converted = 0;
938 for (i = 1; i < argc; i = j) {
939 j = i + 1;
941 if (string_equal(argv[i], "-i")) {
942 /* input format */
944 if (argc < j + 1) {
945 fprintf(stderr, "%s: missing argument for %s flag\n",
946 argv[0], argv[i]);
947 return EXIT_FAILURE;
950 if (parse_format(argv[j], &input_format) != EXIT_SUCCESS) {
951 fprintf(stderr, "%s: unknown date format \"%s\"\n",
952 argv[0], argv[j]);
953 return EXIT_FAILURE;
956 ++j;
958 } else if (string_equal(argv[i], "-o")) {
959 /* output format */
961 if (argc < j + 1) {
962 fprintf(stderr, "%s: missing argument for %s flag\n",
963 argv[0], argv[i]);
964 return EXIT_FAILURE;
967 if (parse_format(argv[j], &output_format) != EXIT_SUCCESS) {
968 fprintf(stderr, "%s: unknown date format \"%s\"\n",
969 argv[0], argv[j]);
970 return EXIT_FAILURE;
973 ++j;
975 } else if (string_equal(argv[i], "-r")) {
976 /* reference date */
978 if (argc < j + 1) {
979 fprintf(stderr,
980 "%s: missing argument for %s flag\n",
981 argv[0], argv[i]);
982 return EXIT_FAILURE;
985 if (parse_date(argv[j], century, wy, input_format,
986 &reference_date, &recognized) != EXIT_SUCCESS) {
987 fprintf(stderr,
988 "%s: unable to parse reference date (%s)\n",
989 argv[0], REFDATE);
990 return EXIT_FAILURE;
993 ++j;
995 } else if (string_equal(argv[i], "-w")) {
996 /* wrap year */
998 if (argc < j + 1) {
999 fprintf(stderr,
1000 "%s: missing argument for %s flag\n",
1001 argv[0], argv[i]);
1002 return EXIT_FAILURE;
1005 century = 100*(1 + atoi(argv[j])/100);
1006 wy = atoi(argv[j]) - (century - 100);
1008 ++j;
1010 } else if (string_equal(argv[i], "-h")) {
1011 /* help */
1012 usage(stderr, argv[0]);
1013 } else {
1014 /* date */
1015 converted = 1;
1016 if (convert_and_write (argv[i], century, wy, reference_date,
1017 input_format, output_format)
1018 != EXIT_SUCCESS) {
1019 fprintf(stderr,
1020 "%s: unable to parse date (%s)\n",
1021 argv[0], argv[i]);
1022 retval = EXIT_FAILURE;
1030 if (converted == 0) {
1031 /* there was no date in the command line : use standard input */
1032 int reading = 1;
1033 int num_line = 0;
1034 int size = 0;
1035 char *line = 0;
1036 expand_line_buffer (&line, &size, NULL);
1038 while (reading) {
1039 /* input lines reading loop */
1040 char *cursor = line + 1;
1041 ++num_line;
1042 line[0] = ' ';
1043 line[1] = '\0';
1045 while (reading != 0 && *(cursor - 1) != '\n') {
1046 /* trying to read until end of line */
1048 if (size - (cursor - line) < 2) {
1049 /* there is not enough room left */
1050 expand_line_buffer(&line, &size, &cursor);
1053 if (fgets(cursor, size - (cursor - line), stdin) == NULL) {
1054 if (cursor == line + 1) {
1055 /* we are at end */
1056 reading = 0;
1057 } else {
1058 /* something went wrong */
1059 fprintf(stderr,
1060 "%s: read error on line %d: %s\n",
1061 argv[0], num_line, line + 1);
1062 retval = EXIT_FAILURE;
1064 } else {
1065 /* something has been successfully read */
1066 cursor += strlen(cursor);
1069 *(cursor - 1) = '\0';
1071 if (reading) {
1072 /* converting the date */
1073 if (convert_and_write (line + 1, century, wy, reference_date,
1074 input_format, output_format)
1075 != EXIT_SUCCESS) {
1076 fprintf(stderr,
1077 "%s: unable to parse date (%s)\n",
1078 argv[0], line + 1);
1079 retval = EXIT_FAILURE;
1085 return retval;