Fix a memory leak. (#94550, Sebastian Rittau)
[platform/upstream/glib.git] / glib / gdate.c
index c3018ec..bfbb714 100644 (file)
@@ -41,7 +41,6 @@
 
 #include <time.h>
 #include <string.h>
-#include <ctype.h>
 #include <stdlib.h>
 #include <locale.h>
 
@@ -100,7 +99,7 @@ g_date_free (GDate *d)
 }
 
 gboolean     
-g_date_valid (GDate       *d)
+g_date_valid (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, FALSE);
   
@@ -167,8 +166,9 @@ g_date_valid_dmy (GDateDay     d,
  *   Jan 1, Year 1
  */
 static void
-g_date_update_julian (GDate *d)
+g_date_update_julian (const GDate *const_d)
 {
+  GDate *d = (GDate *) const_d;
   GDateYear year;
   gint index;
   
@@ -201,8 +201,9 @@ g_date_update_julian (GDate *d)
 }
 
 static void 
-g_date_update_dmy (GDate *d)
+g_date_update_dmy (const GDate *const_d)
 {
+  GDate *d = (GDate *) const_d;
   GDateYear y;
   GDateMonth m;
   GDateDay day;
@@ -249,7 +250,7 @@ g_date_update_dmy (GDate *d)
 }
 
 GDateWeekday 
-g_date_get_weekday (GDate *d)
+g_date_get_weekday (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, G_DATE_BAD_WEEKDAY);
   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
@@ -264,7 +265,7 @@ g_date_get_weekday (GDate *d)
 }
 
 GDateMonth   
-g_date_get_month (GDate *d)
+g_date_get_month (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, G_DATE_BAD_MONTH);
   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
@@ -279,7 +280,7 @@ g_date_get_month (GDate *d)
 }
 
 GDateYear    
-g_date_get_year (GDate *d)
+g_date_get_year (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, G_DATE_BAD_YEAR);
   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
@@ -294,7 +295,7 @@ g_date_get_year (GDate *d)
 }
 
 GDateDay     
-g_date_get_day (GDate *d)
+g_date_get_day (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, G_DATE_BAD_DAY);
   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
@@ -309,7 +310,7 @@ g_date_get_day (GDate *d)
 }
 
 guint32      
-g_date_get_julian (GDate *d)
+g_date_get_julian (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, G_DATE_BAD_JULIAN);
   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
@@ -324,7 +325,7 @@ g_date_get_julian (GDate *d)
 }
 
 guint        
-g_date_get_day_of_year (GDate *d)
+g_date_get_day_of_year (const GDate *d)
 {
   gint index;
   
@@ -343,7 +344,7 @@ g_date_get_day_of_year (GDate *d)
 }
 
 guint        
-g_date_get_monday_week_of_year (GDate *d)
+g_date_get_monday_week_of_year (const GDate *d)
 {
   GDateWeekday wd;
   guint day;
@@ -369,7 +370,7 @@ g_date_get_monday_week_of_year (GDate *d)
 }
 
 guint        
-g_date_get_sunday_week_of_year (GDate *d)
+g_date_get_sunday_week_of_year (const GDate *d)
 {
   GDateWeekday wd;
   guint day;
@@ -395,6 +396,19 @@ g_date_get_sunday_week_of_year (GDate *d)
   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
 }
 
+gint
+g_date_days_between (const GDate *d1,
+                    const GDate *d2)
+{
+  g_return_val_if_fail (d1 != NULL, 0);
+  g_return_val_if_fail (d2 != NULL, 0);
+
+  g_return_val_if_fail (g_date_valid (d1), 0);
+  g_return_val_if_fail (g_date_valid (d2), 0);
+
+  return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
+}
+
 void         
 g_date_clear (GDate       *d, guint ndates)
 {
@@ -471,7 +485,7 @@ g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
     {
       
       i = 0;
-      while (*s && isdigit (*s) && i <= NUM_LEN)
+      while (*s && g_ascii_isdigit (*s) && i <= NUM_LEN)
         {
           num[pt->num_ints][i] = *s;
           ++s; 
@@ -497,38 +511,42 @@ g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
   
   if (pt->num_ints < 3)
     {
-      gchar lcstr[128];
-      int i = 1;
-      
-      strncpy (lcstr, str, 127);
-      g_strdown (lcstr);
+      gchar *casefold;
+      gchar *normalized;
       
+      casefold = g_utf8_casefold (str, -1);
+      normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+      g_free (casefold);
+
+      i = 1;
       while (i < 13)
         {
           if (long_month_names[i] != NULL) 
             {
-              const gchar *found = strstr (lcstr, long_month_names[i]);
+              const gchar *found = strstr (normalized, long_month_names[i]);
              
               if (found != NULL)
                 {
                   pt->month = i;
-                 return;
+                 break;
                 }
             }
          
           if (short_month_names[i] != NULL) 
             {
-              const gchar *found = strstr (lcstr, short_month_names[i]);
+              const gchar *found = strstr (normalized, short_month_names[i]);
              
               if (found != NULL)
                 {
                   pt->month = i;
-                  return;
+                 break;
                 }
             }
 
           ++i;
-        }      
+        }
+
+      g_free (normalized);
     }
 }
 
@@ -561,21 +579,24 @@ g_date_prepare_to_parse (const gchar *str, GDateParseTokens *pt)
       
       while (i < 13) 
         {
+         gchar *casefold;
+         
           g_date_set_dmy (&d, 1, i, 1);
          
           g_return_if_fail (g_date_valid (&d));
          
           g_date_strftime (buf, 127, "%b", &d);
+
+         casefold = g_utf8_casefold (buf, -1);
           g_free (short_month_names[i]);
-          g_strdown (buf);
-          short_month_names[i] = g_strdup (buf);
-         
-          
+          short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+         g_free (casefold);
          
           g_date_strftime (buf, 127, "%B", &d);
+         casefold = g_utf8_casefold (buf, -1);
           g_free (long_month_names[i]);
-          g_strdown (buf);
-          long_month_names[i] = g_strdup (buf);
+          long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+         g_free (casefold);
           
           ++i;
         }
@@ -919,7 +940,7 @@ g_date_set_julian (GDate *d, guint32 j)
 
 
 gboolean     
-g_date_is_first_of_month (GDate *d)
+g_date_is_first_of_month (const GDate *d)
 {
   g_return_val_if_fail (d != NULL, FALSE);
   g_return_val_if_fail (g_date_valid (d), FALSE);
@@ -935,7 +956,7 @@ g_date_is_first_of_month (GDate *d)
 }
 
 gboolean     
-g_date_is_last_of_month (GDate *d)
+g_date_is_last_of_month (const GDate *d)
 {
   gint index;
   
@@ -1184,8 +1205,8 @@ g_date_get_sunday_weeks_in_year (GDateYear  year)
 }
 
 gint         
-g_date_compare (GDate     *lhs, 
-                GDate     *rhs)
+g_date_compare (const GDate *lhs, 
+                const GDate *rhs)
 {
   g_return_val_if_fail (lhs != NULL, 0);
   g_return_val_if_fail (rhs != NULL, 0);
@@ -1235,7 +1256,7 @@ g_date_compare (GDate     *lhs,
 
 
 void        
-g_date_to_struct_tm (GDate      *d, 
+g_date_to_struct_tm (const GDate *d, 
                      struct tm   *tm)
 {
   GDateWeekday day;
@@ -1273,30 +1294,141 @@ g_date_to_struct_tm (GDate      *d,
   tm->tm_isdst = -1; /* -1 means "information not available" */
 }
 
+void
+g_date_clamp (GDate *date,
+             const GDate *min_date,
+             const GDate *max_date)
+{
+  g_return_if_fail (date);
+  g_return_if_fail (g_date_valid (date));
+  if (min_date != NULL)
+    g_return_if_fail (g_date_valid (min_date));
+  if (max_date != NULL)
+    g_return_if_fail (g_date_valid (max_date));
+  if (min_date != NULL && max_date != NULL)
+    g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
+
+  if (min_date && g_date_compare (date, min_date) < 0)
+    *date = *min_date;
+
+  if (max_date && g_date_compare (max_date, date) < 0)
+    *date = *max_date;
+}
+
+void
+g_date_order (GDate *date1,
+              GDate *date2)
+{
+  g_return_if_fail (date1 != NULL);
+  g_return_if_fail (date2 != NULL);
+  g_return_if_fail (g_date_valid (date1));
+  g_return_if_fail (g_date_valid (date2));
+
+  if (g_date_compare (date1, date2) > 0)
+    {
+      GDate tmp = *date1;
+      *date1 = *date2;
+      *date2 = tmp;
+    }
+}
+
 gsize     
 g_date_strftime (gchar       *s, 
                  gsize        slen, 
                  const gchar *format, 
-                 GDate       *d)
+                 const GDate *d)
 {
   struct tm tm;
+  gsize locale_format_len = 0;
+  gchar *locale_format;
+  gsize tmplen;
+  gchar *tmpbuf;
+  gsize tmpbufsize;
+  gsize convlen = 0;
+  gchar *convbuf;
+  GError *error = NULL;
   gsize retval;
-  
+
   g_return_val_if_fail (d != NULL, 0);
   g_return_val_if_fail (g_date_valid (d), 0);
   g_return_val_if_fail (slen > 0, 0); 
   g_return_val_if_fail (format != 0, 0);
   g_return_val_if_fail (s != 0, 0);
-  
+
   g_date_to_struct_tm (d, &tm);
-  
-  retval = strftime (s, slen, format, &tm);
-  if (retval == 0)
+
+  locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
+
+  if (error)
+    {
+      g_warning (G_STRLOC "Error converting format to locale encoding: %s\n", error->message);
+      g_error_free (error);
+
+      s[0] = '\0';
+      return 0;
+    }
+
+  tmpbufsize = MAX (128, locale_format_len * 2);
+  while (TRUE)
     {
-      /* If retval == 0, the contents of s are undefined.  We define
-       *  them. 
+      tmpbuf = g_malloc (tmpbufsize);
+
+      /* Set the first byte to something other than '\0', to be able to
+       * recognize whether strftime actually failed or just returned "".
        */
+      tmpbuf[0] = '\1';
+      tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
+
+      if (tmplen == 0 && tmpbuf[0] != '\0')
+        {
+          g_free (tmpbuf);
+          tmpbufsize *= 2;
+
+          if (tmpbufsize > 65536)
+            {
+              g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up\n");
+              g_free (locale_format);
+
+              s[0] = '\0';
+              return 0;
+            }
+        }
+      else
+        break;
+    }
+  g_free (locale_format);
+
+  convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
+  g_free (tmpbuf);
+
+  if (error)
+    {
+      g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s\n", error->message);
+      g_error_free (error);
+
       s[0] = '\0';
+      return 0;
     }
+
+  if (slen <= convlen)
+    {
+      /* Ensure only whole characters are copied into the buffer.
+       */
+      gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
+      g_assert (end != NULL);
+      convlen = end - convbuf;
+
+      /* Return 0 because the buffer isn't large enough.
+       */
+      retval = 0;
+    }
+  else
+    retval = convlen;
+
+  memcpy (s, convbuf, convlen);
+  s[convlen] = '\0';
+  g_free (convbuf);
+
   return retval;
 }
+