add boilerplate for standard licenses
[gnulib.git] / lib / getdate.y
index 695fd59..2f3c924 100644 (file)
@@ -1,7 +1,7 @@
 %{
 /* Parse a string into an internal time stamp.
 
-   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -60,7 +60,7 @@
 # undef static
 #endif
 
-#include <ctype.h>
+#include <c-ctype.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -205,7 +205,7 @@ typedef struct
 union YYSTYPE;
 static int yylex (union YYSTYPE *, parser_control *);
 static int yyerror (parser_control const *, char const *);
-static long int time_zone_hhmm (textint, long int);
+static long int time_zone_hhmm (parser_control *, textint, long int);
 
 /* Extract into *PC any date and time info from a string of digits
    of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
@@ -293,7 +293,7 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes,
 %token tAGO tDST
 
 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
-%token <intval> tDAY_UNIT
+%token <intval> tDAY_UNIT tDAY_SHIFT
 
 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
 %token <intval> tMONTH tORDINAL tZONE
@@ -304,7 +304,7 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes,
 %type <intval> o_colon_minutes o_merid
 %type <timespec> seconds signed_seconds unsigned_seconds
 
-%type <rel> relunit relunit_snumber
+%type <rel> relunit relunit_snumber dayshift
 
 %%
 
@@ -358,7 +358,7 @@ time:
        set_hhmmss (pc, $1.value, $3.value, 0, 0);
        pc->meridian = MER24;
        pc->zones_seen++;
-       pc->time_zone = time_zone_hhmm ($4, $5);
+       pc->time_zone = time_zone_hhmm (pc, $4, $5);
       }
   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
       {
@@ -370,7 +370,7 @@ time:
        set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
        pc->meridian = MER24;
        pc->zones_seen++;
-       pc->time_zone = time_zone_hhmm ($6, $7);
+       pc->time_zone = time_zone_hhmm (pc, $6, $7);
       }
   ;
 
@@ -394,7 +394,7 @@ zone:
       { pc->time_zone = $1;
        apply_relative_time (pc, $2, 1); }
   | tZONE tSNUMBER o_colon_minutes
-      { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
+      { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
   | tDAYZONE
       { pc->time_zone = $1 + 60; }
   | tZONE tDST
@@ -404,12 +404,12 @@ zone:
 day:
     tDAY
       {
-       pc->day_ordinal = 1;
+       pc->day_ordinal = 0;
        pc->day_number = $1;
       }
   | tDAY ','
       {
-       pc->day_ordinal = 1;
+       pc->day_ordinal = 0;
        pc->day_number = $1;
       }
   | tORDINAL tDAY
@@ -502,6 +502,8 @@ rel:
       { apply_relative_time (pc, $1, -1); }
   | relunit
       { apply_relative_time (pc, $1, 1); }
+  | dayshift
+      { apply_relative_time (pc, $1, 1); }
   ;
 
 relunit:
@@ -563,6 +565,11 @@ relunit_snumber:
       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
   ;
 
+dayshift:
+    tDAY_SHIFT
+      { $$ = RELATIVE_TIME_0; $$.day = $1; }
+  ;
+
 seconds: signed_seconds | unsigned_seconds;
 
 signed_seconds:
@@ -669,10 +676,10 @@ static table const time_units_table[] =
 /* Assorted relative-time words. */
 static table const relative_time_table[] =
 {
-  { "TOMORROW",        tDAY_UNIT,       1 },
-  { "YESTERDAY",tDAY_UNIT,     -1 },
-  { "TODAY",   tDAY_UNIT,       0 },
-  { "NOW",     tDAY_UNIT,       0 },
+  { "TOMORROW",        tDAY_SHIFT,      1 },
+  { "YESTERDAY",tDAY_SHIFT,    -1 },
+  { "TODAY",   tDAY_SHIFT,      0 },
+  { "NOW",     tDAY_SHIFT,      0 },
   { "LAST",    tORDINAL,       -1 },
   { "THIS",    tORDINAL,        0 },
   { "NEXT",    tORDINAL,        1 },
@@ -795,15 +802,33 @@ static table const military_table[] =
 
 /* Convert a time zone expressed as HH:MM into an integer count of
    minutes.  If MM is negative, then S is of the form HHMM and needs
-   to be picked apart; otherwise, S is of the form HH.  */
+   to be picked apart; otherwise, S is of the form HH.  As specified in
+   http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
+   only valid TZ range, and consider first two digits as hours, if no
+   minutes specified.  */
 
 static long int
-time_zone_hhmm (textint s, long int mm)
+time_zone_hhmm (parser_control *pc, textint s, long int mm)
 {
+  long int n_minutes;
+
+  /* If the length of S is 1 or 2 and no minutes are specified,
+     interpret it as a number of hours.  */
+  if (s.digits <= 2 && mm < 0)
+    s.value *= 100;
+
   if (mm < 0)
-    return (s.value / 100) * 60 + s.value % 100;
+    n_minutes = (s.value / 100) * 60 + s.value % 100;
   else
-    return s.value * 60 + (s.negative ? -mm : mm);
+    n_minutes = s.value * 60 + (s.negative ? -mm : mm);
+
+  /* If the absolute number of minutes is larger than 24 hours,
+     arrange to reject it by incrementing pc->zones_seen.  Thus,
+     we allow only values in the range UTC-24:00 to UTC+24:00.  */
+  if (24 * 60 < abs (n_minutes))
+    pc->zones_seen++;
+
+  return n_minutes;
 }
 
 static int
@@ -900,7 +925,7 @@ lookup_word (parser_control const *pc, char *word)
   for (p = word; *p; p++)
     {
       unsigned char ch = *p;
-      *p = toupper (ch);
+      *p = c_toupper (ch);
     }
 
   for (tp = meridian_table; tp->name; tp++)
@@ -965,7 +990,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc)
 
   for (;;)
     {
-      while (c = *pc->input, isspace (c))
+      while (c = *pc->input, c_isspace (c))
        pc->input++;
 
       if (ISDIGIT (c) || c == '-' || c == '+')
@@ -976,7 +1001,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc)
          if (c == '-' || c == '+')
            {
              sign = c == '-' ? -1 : 1;
-             while (c = *++pc->input, isspace (c))
+             while (c = *++pc->input, c_isspace (c))
                continue;
              if (! ISDIGIT (c))
                /* skip the '-' sign */
@@ -1080,7 +1105,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc)
            }
        }
 
-      if (isalpha (c))
+      if (c_isalpha (c))
        {
          char buff[20];
          char *p = buff;
@@ -1092,7 +1117,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc)
                *p++ = c;
              c = *++pc->input;
            }
-         while (isalpha (c) || c == '.');
+         while (c_isalpha (c) || c == '.');
 
          *p = '\0';
          tp = lookup_word (pc, buff);
@@ -1205,7 +1230,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
   if (! tmp)
     return false;
 
-  while (c = *p, isspace (c))
+  while (c = *p, c_isspace (c))
     p++;
 
   if (strncmp (p, "TZ=\"", 4) == 0)
@@ -1410,7 +1435,9 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
       if (pc.days_seen && ! pc.dates_seen)
        {
          tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
-                        + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+                        + 7 * (pc.day_ordinal
+                               - (0 < pc.day_ordinal
+                                  && tm.tm_wday != pc.day_number)));
          tm.tm_isdst = -1;
          Start = mktime (&tm);
          if (Start == (time_t) -1)