Logo Search packages:      
Sourcecode: galeon version File versions

gul-string.c

/*
 *  Copyright (C) 2002 Marco Pesenti Gritti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Galeon includes */
#include "gul-string.h"

/* system includes */
#include <glib/gi18n.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libxml/parser.h>
#include <time.h>
#include <string.h>
#include <errno.h>

/* precomputed g_unichar_to_utf8 (8230, ellipsis); */
#define ELLIPSIS "\xe2\x80\xa6"

/**
 * gul_string_double_underscores: escape _'s in string such that the
 * gtk_label_parse_uline will display as normal.  caller is responsible for
 * freeing the returned string
 */
char *
gul_string_double_underscores (const char *string)
{
        int underscores;
        const char *p;
        char *q;
        char *escaped;
        
        if (string == NULL) {
                return NULL;
        }
        
        underscores = 0;
        for (p = string; *p != '\0'; p++) {
                underscores += (*p == '_');
        }
        
        if (underscores == 0) {
                return g_strdup (string);
        }

        escaped = g_new (char, strlen (string) + underscores + 1);
        for (p = string, q = escaped; *p != '\0'; p++, q++) {
                /* Add an extra underscore. */
                if (*p == '_') {
                        *q++ = '_';
                }
                *q = *p;
        }
        *q = '\0';
        
        return escaped;
}

/**
 * gul_string_new_num_accel: creates a string with a numbered/lettered
 * accel (caller must free). returns NULL if num is out of the range of
 * acceleratable nums/letters
 */
gchar *
gul_string_new_num_accel (gint num, gchar *text, gboolean lettersok)
{
      gchar *label = NULL;

      if (num < 9)
            label = g_strdup_printf ("_%i. %s", num+1, text);
      else if (num == 9)
            label = g_strdup_printf ("_%i. %s", 0, text);
      else if (num < 36 && lettersok)
            label = g_strdup_printf ("_%c. %s", 'a'+num-10, text);
      return label;
}

/**
 * gul_string_strip_uline_accel: strip the _ out of a string like
 * gtk_label_parse_uline would do.  caller is responsible for freeing the
 * returned string
 */
gchar *
gul_string_strip_uline_accel (const gchar *text)
{
      GString *out;
      const gchar *u, *cur = text;

      out = g_string_new (NULL);
      while ((u = strchr (cur, '_')))
      {
            if (*(u+1) == '_')
            {
                  /* __ in the string is equal to _ in the output 
                   * so include the _ in the output, skip over the 
                   * second _ and continue scanning. */
                  g_string_append_printf (out, "%.*s", u - cur + 1, cur);
                  cur = u + 2;
            } else {
                  /* copy everything before the _ and skip over it */
                  g_string_append_printf (out, "%.*s", u - cur , cur);
                  cur = u + 1;
                  /* only one accel per string, so we are done now */
                  break;
            }
      }
      if (cur && *cur)
      {
            /* attach rest of string */
            g_string_append (out, cur);
      }

      u = out->str;
      g_string_free (out, FALSE); /* don't free char data, caller must */
      return (gchar *)u;
}

/**
 * gul_string_shorten: returns a newly allocated shortened version of str.
 * the new string will be no longer than target_length characters, and will
 * be of the form "http://blahblah...blahblah.html".
 */
gchar *
gul_string_shorten (const gchar *str, gint target_length)
{
      GString *new_str;
      gchar *ret;
      gint actual_length, first_length, second_length;

      if (!str) return NULL;

      actual_length = g_utf8_strlen (str, -1);

      /* if the string is already short enough return a new copy */
      if (actual_length <= target_length)
            return g_strdup (str);

      /* calc lengths to take from beginning and ending of str
       * (1 == ellipsis is only one character) */
      second_length = (target_length - 1) / 2;
      first_length = target_length - 1 - second_length;

      /* create string */
      new_str = g_string_sized_new (target_length);
      g_string_append_len (new_str, str, 
                       (g_utf8_offset_to_pointer (str, first_length) - str));
      g_string_append (new_str, ELLIPSIS);
      g_string_append (new_str, g_utf8_offset_to_pointer (str, actual_length - second_length));

      ret = new_str->str;
      g_string_free (new_str, FALSE);

      return ret;
}

/**
 * gul_string_strcasestr: test if a string b is a substring of string a,
 * independent of case (ascii).
 */
const gchar *
gul_string_ascii_strcasestr (const gchar *a, const gchar *b)
{
      gchar *down_a;
      gchar *down_b;
      gchar *ptr;

      /* copy and lower case the strings */
      down_a = g_strdup (a);
      down_b = g_strdup (b);
      g_ascii_strdown (down_a, -1);
      g_ascii_strdown (down_b, -1);

      /* compare */
      ptr = strstr (down_a, down_b);

      /* free allocated strings */
      g_free (down_a);
      g_free (down_b);
      
      /* return result of comparison */
      return ptr == NULL ? NULL : (a + (ptr - down_a));
}

/**
 * gul_string_strdup_replace: returns a new string (which must be freed
 * later) identical to str, but with all occurrences of a replaced by b.
 * if any of the given strings are null, or if a is empty, null is
 * returned. */
gchar *
gul_string_strdup_replace (const gchar *str, const gchar *a, const gchar *b)
{
      gchar *new_str, *start_str, *end_str, *dest_str;
      gint str_len, a_len, b_len, new_str_len;

      /* return if any of the strings are null, or if a is empty */
      if (!str || !a || !b || a[0] == '\0') return NULL;

      /* get the lengths of the user-supplied strings */
      str_len = new_str_len = strlen (str);
      a_len = strlen (a);
      b_len = strlen (b);

      /* find the length of the new string */
      start_str = (gchar *) str;
      while ((start_str = strstr (start_str, a)))
      {
            new_str_len -= a_len;
            new_str_len += b_len;
            start_str = &(start_str[a_len]);
      }

      /* allocate memory for the new string */
      new_str = g_new0 (gchar, new_str_len + 1);
      g_return_val_if_fail (new_str, NULL);

      /* walk through the string, replacing a with b */
      start_str = (gchar *) str;
      dest_str = new_str;
      while ((end_str = strstr (start_str, a)))
      {
            /* copy the stuff preceding a */
            memcpy (dest_str, start_str, end_str - start_str);
            dest_str = &(dest_str[end_str - start_str]);
            /* copy b in the place of a */
            memcpy (dest_str, b, b_len);
            dest_str = &(dest_str[b_len]);
            /* move to the end of the occurrence of a */
            start_str = &(end_str[a_len]);
      }

      /* copy the last part of the string and terminate it */
      memcpy (dest_str, start_str, strlen (start_str));
      dest_str[strlen (start_str)] = '\0';

      return new_str;
}

/**
 * gul_string_store_time_in_string:
 * NOTE: str must be at least 256 chars long
 */
void
gul_string_store_time_in_string (GTime t, gchar *str)
{
      int length;

      if (t > 0)
      {
            GDate * date = g_date_new ();
            g_date_set_time (date, t);
            
            length = g_date_strftime (str, 255, "%x", date);
            str[length] = '\0';

            g_date_free (date);
      }
      else
      {
            str[0] = '\0';
      }
}

/**
 * gul_string_strip_newline: strip newline from an utf8 string
 */
gchar *
gul_string_strip_newline (const gchar *c)
{
      /* FIXME ??? */
      return g_strdup (c);
}

/**
 * gul_string_remove_outside_whitespace: removes leading and trailing
 * whitespace.  returns a newly-allocated string.
 */
gchar *
gul_string_remove_outside_whitespace (const gchar *str)
{
      gint start, end;

      for (start = 0; str[start] != '\0' && (str[start] == ' ' ||
            str[start] == '\n' || str[start] == '\t' ||
            str[start] == '\r'); start++);
      for (end = strlen (str) - 1; end > start && (str[end] == ' ' ||
            str[end] == '\n' || str[end] == '\t' ||
            str[end] == '\r'); end--);
      return g_strndup (&(str[start]), end + 1 - start);
}

/**
 * gul_string_expand_home_dir: expands a leading ~ in the string to the
 * user's home directory.  doesn't handle ~some_user syntax.  returns a
 * newly-allocated string.
 */
gchar *
gul_string_expand_home_dir (const gchar *str)
{
        if (!str) return NULL;
        if (str[0] != '~') return g_strdup (str);

        return g_strconcat (g_get_home_dir (), str + 1, NULL);
}

int
gul_strcasecmp (const char *string_a, const char *string_b)
{
        return g_ascii_strcasecmp (string_a == NULL ? "" : string_a,
                                   string_b == NULL ? "" : string_b);
}

int
gul_strcasecmp_compare_func (gconstpointer string_a, gconstpointer string_b)
{
        return gul_strcasecmp ((const char *) string_a,
                         (const char *) string_b);
}

/**
 * like strpbrk but ignores chars preceded by slashes, unless the
 * slash is also preceded by a slash unless that later slash is
 * preceded by another slash... ;-)
 */
static char *
gul_strpbrk_unescaped (const char *s, const char *accept)
{
      gchar *ret = strpbrk (s, accept);

      if (!ret || ret == s || *(ret - 1) != '\\')
      {
            return ret;
      }
      else
      {
            gchar *c = ret - 1;
            g_assert (*c == '\\');
            
            while (c >= s && *c == '\\') c--;

            if ((ret - c) % 2 == 0)
            {
                  return gul_strpbrk_unescaped (ret + 1, accept);
            }
            else
            {
                  return ret;
            }
      }
}

/**
 * like strstr but supports quoting, ignoring matches inside quoted text
 */
static char *
gul_strstr_with_quotes (const char *haystack, const char *needle,
                  const char *quotes)
{
      gchar *quot = gul_strpbrk_unescaped (haystack, quotes);
      gchar *ret = strstr (haystack, needle);

      if (!quot || !ret || ret < quot)
      {
            return ret;
      }

      quot = gul_strpbrk_unescaped (quot + 1, quotes);

      if (quot) 
      {
            return gul_strstr_with_quotes (quot + 1, needle, quotes);
      }
      else
      {
            return NULL;
      }
}

/**
 * like strpbrk but supports quoting, ignoring matches inside quoted text
 */
static char *
gul_strpbrk_with_quotes (const char *haystack, const char *needles,
                  const char *quotes)
{
      gchar *quot = gul_strpbrk_unescaped (haystack, quotes);
      gchar *ret = strpbrk (haystack, needles);

      if (!quot || !ret || ret < quot)
      {
            return ret;
      }

      quot = gul_strpbrk_unescaped (quot + 1, quotes);

      if (quot) 
      {
            return gul_strpbrk_with_quotes (quot + 1, needles, quotes);
      }
      else
      {
            return NULL;
      }
}

/**
 * Like g_strsplit, but does not split tokens betwen quotes. Ignores
 * quotes preceded by '\'.
 */
gchar **
gul_strsplit_with_quotes (const gchar *string,
                    const gchar *delimiter,
                    gint max_tokens,
                    const gchar *quotes)
{
      GSList *string_list = NULL, *slist;
      gchar **str_array, *s;
      guint n = 0;
      const gchar *remainder;
      
      g_return_val_if_fail (string != NULL, NULL);
      g_return_val_if_fail (delimiter != NULL, NULL);
      g_return_val_if_fail (delimiter[0] != '\0', NULL);

      if (quotes == NULL)
      {
            return g_strsplit (string, delimiter, max_tokens);
      }

      if (max_tokens < 1)
      {
            max_tokens = G_MAXINT;
      }

      remainder = string;
      s = gul_strstr_with_quotes (remainder, delimiter, quotes);
      if (s)
      {
            gsize delimiter_len = strlen (delimiter);   
            
            while (--max_tokens && s)
            {
                  gsize len;     
                  gchar *new_string;
                  
                  len = s - remainder;
                  new_string = g_new (gchar, len + 1);
                  strncpy (new_string, remainder, len);
                  new_string[len] = 0;
                  string_list = g_slist_prepend (string_list, new_string);
                  n++;
                  remainder = s + delimiter_len;
                  s = gul_strstr_with_quotes (remainder, delimiter, quotes);
            }
      }
      if (*string)
      {
            n++;
            string_list = g_slist_prepend (string_list, g_strdup (remainder));
      }
      
      str_array = g_new (gchar*, n + 1);
      
      str_array[n--] = NULL;
      for (slist = string_list; slist; slist = slist->next)
      {
            str_array[n--] = slist->data;
      }
      
      g_slist_free (string_list);
      
      return str_array;
}

/**
 * like gul_strsplit_with_quotes, but matches any char in 'delimiters' as delimiter
 * and does not return empty tokens
 */
gchar **
gul_strsplit_multiple_delimiters_with_quotes (const gchar *string,
                                    const gchar *delimiters,
                                    gint max_tokens,
                                    const gchar *quotes)
{
      GSList *string_list = NULL, *slist;
      gchar **str_array, *s;
      guint n = 0;
      const gchar *remainder;
      
      g_return_val_if_fail (string != NULL, NULL);
      g_return_val_if_fail (delimiters != NULL, NULL);
      g_return_val_if_fail (delimiters[0] != '\0', NULL);

      if (quotes == NULL)
      {
            quotes = "";
      }

      if (max_tokens < 1)
      {
            max_tokens = G_MAXINT;
      }

      remainder = string;
      s = gul_strpbrk_with_quotes (remainder, delimiters, quotes);
      if (s)
      {
            const gsize delimiter_len = 1; /* only chars */
            
            while (--max_tokens && s)
            {
                  gsize len;     
                  gchar *new_string;
                  
                  len = s - remainder;
                  if (len > 0) /* ignore empty strings */
                  {
                        new_string = g_new (gchar, len + 1);
                        strncpy (new_string, remainder, len);
                        new_string[len] = 0;
                        string_list = g_slist_prepend (string_list, new_string);
                        n++;
                  }
                  remainder = s + delimiter_len;
                  s = gul_strpbrk_with_quotes (remainder, delimiters, quotes);
            }
      }
      if (*string)
      {
            n++;
            string_list = g_slist_prepend (string_list, g_strdup (remainder));
      }
      
      str_array = g_new (gchar*, n + 1);
      
      str_array[n--] = NULL;
      for (slist = string_list; slist; slist = slist->next)
      {
            str_array[n--] = slist->data;
      }
      
      g_slist_free (string_list);
      
      return str_array;
}

Generated by  Doxygen 1.6.0   Back to index