static inline gboolean
calendar_appointment_equal (CalendarAppointment *a,
                            CalendarAppointment *b)
{
  GSList *la, *lb;

  if (g_slist_length (a->occurrences) != g_slist_length (b->occurrences))
      return FALSE;

  for (la = a->occurrences, lb = b->occurrences; la && lb; la = la->next, lb = lb->next)
    {
      CalendarOccurrence *oa = la->data;
      CalendarOccurrence *ob = lb->data;

      if (oa->start_time != ob->start_time ||
          oa->end_time   != ob->end_time)
        return FALSE;
    }

  return
    null_safe_strcmp (a->uid,          b->uid)          == 0 &&
    null_safe_strcmp (a->backend_name, b->backend_name) == 0 &&
    null_safe_strcmp (a->summary,      b->summary)      == 0 &&
    null_safe_strcmp (a->description,  b->description)  == 0 &&
    null_safe_strcmp (a->color_string, b->color_string) == 0 &&
    a->start_time == b->start_time                         &&
    a->end_time   == b->end_time                           &&
    a->is_all_day == b->is_all_day;
}
int
mateconf_value_compare (const MateConfValue *value_a,
                     const MateConfValue *value_b)
{
  g_return_val_if_fail (value_a != NULL, 0);
  g_return_val_if_fail (value_b != NULL, 0);

  /* Impose arbitrary type ordering, just to keep the
   * sort invariants stable.
   */
  if (value_a->type < value_b->type)
    return -1;
  else if (value_a->type > value_b->type)
    return 1;
  
  switch (value_a->type)
    {
    case MATECONF_VALUE_INT:
      if (mateconf_value_get_int (value_a) < mateconf_value_get_int (value_b))
        return -1;
      else if (mateconf_value_get_int (value_a) > mateconf_value_get_int (value_b))
        return 1;
      else
        return 0;
    case MATECONF_VALUE_FLOAT:
      if (mateconf_value_get_float (value_a) < mateconf_value_get_float (value_b))
        return -1;
      else if (mateconf_value_get_float (value_a) > mateconf_value_get_float (value_b))
        return 1;
      else
        return 0;
    case MATECONF_VALUE_STRING:
      return strcmp (mateconf_value_get_string (value_a),
                     mateconf_value_get_string (value_b));
    case MATECONF_VALUE_BOOL:
      if (mateconf_value_get_bool (value_a) == mateconf_value_get_bool (value_b))
        return 0;
      /* make TRUE > FALSE to maintain sort invariants */
      else if (mateconf_value_get_bool (value_a))
        return 1;
      else
        return -1;
    case MATECONF_VALUE_LIST:
      {
        GSList *list_a;
        GSList *list_b;

        list_a = mateconf_value_get_list (value_a);
        list_b = mateconf_value_get_list (value_b);
        
        while (list_a != NULL && list_b != NULL)
          {
            int result;

            result = mateconf_value_compare (list_a->data, list_b->data);

            if (result != 0)
              return result;
            
            list_a = g_slist_next (list_a);
            list_b = g_slist_next (list_b);
          }
        
        if (list_a)
          return 1; /* list_a is longer so "greater" */
        else if (list_b)
          return -1;
        else
          return 0;
      }
    case MATECONF_VALUE_PAIR:
      {
        MateConfValue *a_car, *b_car, *a_cdr, *b_cdr;
        int result;
        
        a_car = mateconf_value_get_car (value_a);
        b_car = mateconf_value_get_car (value_b);
        a_cdr = mateconf_value_get_cdr (value_a);
        b_cdr = mateconf_value_get_cdr (value_b);

        if (a_car == NULL && b_car != NULL)
          return -1;
        else if (a_car != NULL && b_car == NULL)
          return 1;
        else if (a_car != NULL && b_car != NULL)
          {
            result = mateconf_value_compare (a_car, b_car);

            if (result != 0)
              return result;
          }

        if (a_cdr == NULL && b_cdr != NULL)
          return -1;
        else if (a_cdr != NULL && b_cdr == NULL)
          return 1;
        else if (a_cdr != NULL && b_cdr != NULL)
          {
            result = mateconf_value_compare (a_cdr, b_cdr);

            if (result != 0)
              return result;
          }

        return 0;
      }
    case MATECONF_VALUE_INVALID:
      return 0;
    case MATECONF_VALUE_SCHEMA:
      {
        const char *locale_a, *locale_b;
        MateConfValueType type_a, type_b;
        MateConfValueType list_type_a, list_type_b;
        MateConfValueType car_type_a, car_type_b;
        MateConfValueType cdr_type_a, cdr_type_b;
        const char *short_desc_a, *short_desc_b;
        const char *long_desc_a, *long_desc_b;
        int result;
        
        type_a = mateconf_schema_get_type (mateconf_value_get_schema (value_a));
        type_b = mateconf_schema_get_type (mateconf_value_get_schema (value_b));

        if (type_a < type_b)
          return -1;
        else if (type_a > type_b)
          return 1;

        short_desc_a = mateconf_schema_get_short_desc (mateconf_value_get_schema (value_a));
        short_desc_b = mateconf_schema_get_short_desc (mateconf_value_get_schema (value_b));

        result = null_safe_strcmp (short_desc_a, short_desc_b);
        if (result != 0)
          return result;
        
        long_desc_a = mateconf_schema_get_long_desc (mateconf_value_get_schema (value_a));


        long_desc_b = mateconf_schema_get_long_desc (mateconf_value_get_schema (value_b));

        result = null_safe_strcmp (long_desc_a, long_desc_b);
        if (result != 0)
          return result;
        
        locale_a = mateconf_schema_get_locale (mateconf_value_get_schema (value_a));
        locale_b = mateconf_schema_get_locale (mateconf_value_get_schema (value_b));

        result = null_safe_strcmp (locale_a, locale_b);
        if (result != 0)
          return result;        

        if (type_a == MATECONF_VALUE_LIST)
          {
            list_type_a = mateconf_schema_get_list_type (mateconf_value_get_schema (value_a));
            list_type_b = mateconf_schema_get_list_type (mateconf_value_get_schema (value_b));
            
            if (list_type_a < list_type_b)
              return -1;
            else if (list_type_a > list_type_b)
              return 1;
          }

        if (type_a == MATECONF_VALUE_PAIR)
          {
            car_type_a = mateconf_schema_get_car_type (mateconf_value_get_schema (value_a));
            car_type_b = mateconf_schema_get_car_type (mateconf_value_get_schema (value_b));
            
            if (car_type_a < car_type_b)
              return -1;
            else if (car_type_a > car_type_b)
              return 1;
            
            cdr_type_a = mateconf_schema_get_cdr_type (mateconf_value_get_schema (value_a));
            cdr_type_b = mateconf_schema_get_cdr_type (mateconf_value_get_schema (value_b));
            
            if (cdr_type_a < cdr_type_b)
              return -1;
            else if (cdr_type_a > cdr_type_b)
              return 1;
          }

        return 0;
      }
    }

  g_assert_not_reached ();

  return 0;
}