Blame glib/deprecated/gcompletion.c

Packit ae235b
/* GLIB - Library of useful routines for C programming
Packit ae235b
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General Public
Packit ae235b
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 */
Packit ae235b
Packit ae235b
/*
Packit ae235b
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
Packit ae235b
 * file for a list of people on the GLib Team.  See the ChangeLog
Packit ae235b
 * files for a list of changes.  These files are distributed with
Packit ae235b
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
Packit ae235b
 */
Packit ae235b
Packit ae235b
/* 
Packit ae235b
 * MT safe
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
/* we know we are deprecated here, no need for warnings */
Packit ae235b
#define GLIB_DISABLE_DEPRECATION_WARNINGS
Packit ae235b
Packit ae235b
#include "gcompletion.h"
Packit ae235b
Packit ae235b
#include <glib/gstrfuncs.h>
Packit ae235b
#include <glib/gmessages.h>
Packit ae235b
#include <glib/gunicode.h>
Packit ae235b
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * SECTION:completion
Packit ae235b
 * @title: Automatic String Completion
Packit ae235b
 * @short_description: support for automatic completion using a group
Packit ae235b
 *                     of target strings
Packit ae235b
 *
Packit ae235b
 * #GCompletion provides support for automatic completion of a string
Packit ae235b
 * using any group of target strings. It is typically used for file
Packit ae235b
 * name completion as is common in many UNIX shells.
Packit ae235b
 *
Packit ae235b
 * A #GCompletion is created using g_completion_new(). Target items are
Packit ae235b
 * added and removed with g_completion_add_items(),
Packit ae235b
 * g_completion_remove_items() and g_completion_clear_items(). A
Packit ae235b
 * completion attempt is requested with g_completion_complete() or
Packit ae235b
 * g_completion_complete_utf8(). When no longer needed, the
Packit ae235b
 * #GCompletion is freed with g_completion_free().
Packit ae235b
 *
Packit ae235b
 * Items in the completion can be simple strings (e.g. filenames), or
Packit ae235b
 * pointers to arbitrary data structures. If data structures are used
Packit ae235b
 * you must provide a #GCompletionFunc in g_completion_new(), which
Packit ae235b
 * retrieves the item's string from the data structure. You can change
Packit ae235b
 * the way in which strings are compared by setting a different
Packit ae235b
 * #GCompletionStrncmpFunc in g_completion_set_compare().
Packit ae235b
 *
Packit ae235b
 * GCompletion has been marked as deprecated, since this API is rarely
Packit ae235b
 * used and not very actively maintained.
Packit ae235b
 **/
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * GCompletion:
Packit ae235b
 * @items: list of target items (strings or data structures).
Packit ae235b
 * @func: function which is called to get the string associated with a
Packit ae235b
 *        target item. It is %NULL if the target items are strings.
Packit ae235b
 * @prefix: the last prefix passed to g_completion_complete() or
Packit ae235b
 *          g_completion_complete_utf8().
Packit ae235b
 * @cache: the list of items which begin with @prefix.
Packit ae235b
 * @strncmp_func: The function to use when comparing strings.  Use
Packit ae235b
 *                g_completion_set_compare() to modify this function.
Packit ae235b
 *
Packit ae235b
 * The data structure used for automatic completion.
Packit ae235b
 **/
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * GCompletionFunc:
Packit ae235b
 * @Param1: the completion item.
Packit ae235b
 *
Packit ae235b
 * Specifies the type of the function passed to g_completion_new(). It
Packit ae235b
 * should return the string corresponding to the given target item.
Packit ae235b
 * This is used when you use data structures as #GCompletion items.
Packit ae235b
 *
Packit ae235b
 * Returns: the string corresponding to the item.
Packit ae235b
 **/
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * GCompletionStrncmpFunc:
Packit ae235b
 * @s1: string to compare with @s2.
Packit ae235b
 * @s2: string to compare with @s1.
Packit ae235b
 * @n: maximal number of bytes to compare.
Packit ae235b
 *
Packit ae235b
 * Specifies the type of the function passed to
Packit ae235b
 * g_completion_set_compare(). This is used when you use strings as
Packit ae235b
 * #GCompletion items.
Packit ae235b
 *
Packit ae235b
 * Returns: an integer less than, equal to, or greater than zero if
Packit ae235b
 *          the first @n bytes of @s1 is found, respectively, to be
Packit ae235b
 *          less than, to match, or to be greater than the first @n
Packit ae235b
 *          bytes of @s2.
Packit ae235b
 **/
Packit ae235b
Packit ae235b
static void completion_check_cache (GCompletion* cmp,
Packit ae235b
				    gchar**	 new_prefix);
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_new:
Packit ae235b
 * @func: the function to be called to return the string representing
Packit ae235b
 *        an item in the #GCompletion, or %NULL if strings are going to
Packit ae235b
 *        be used as the #GCompletion items.
Packit ae235b
 *
Packit ae235b
 * Creates a new #GCompletion.
Packit ae235b
 *
Packit ae235b
 * Returns: the new #GCompletion.
Packit ae235b
 **/
Packit ae235b
GCompletion* 
Packit ae235b
g_completion_new (GCompletionFunc func)
Packit ae235b
{
Packit ae235b
  GCompletion* gcomp;
Packit ae235b
  
Packit ae235b
  gcomp = g_new (GCompletion, 1);
Packit ae235b
  gcomp->items = NULL;
Packit ae235b
  gcomp->cache = NULL;
Packit ae235b
  gcomp->prefix = NULL;
Packit ae235b
  gcomp->func = func;
Packit ae235b
  gcomp->strncmp_func = strncmp;
Packit ae235b
Packit ae235b
  return gcomp;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_add_items:
Packit ae235b
 * @cmp: the #GCompletion.
Packit ae235b
 * @items: (transfer none): the list of items to add.
Packit ae235b
 *
Packit ae235b
 * Adds items to the #GCompletion.
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
void 
Packit ae235b
g_completion_add_items (GCompletion* cmp,
Packit ae235b
			GList*	     items)
Packit ae235b
{
Packit ae235b
  GList* it;
Packit ae235b
  
Packit ae235b
  g_return_if_fail (cmp != NULL);
Packit ae235b
  
Packit ae235b
  /* optimize adding to cache? */
Packit ae235b
  if (cmp->cache)
Packit ae235b
    {
Packit ae235b
      g_list_free (cmp->cache);
Packit ae235b
      cmp->cache = NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (cmp->prefix)
Packit ae235b
    {
Packit ae235b
      g_free (cmp->prefix);
Packit ae235b
      cmp->prefix = NULL;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  it = items;
Packit ae235b
  while (it)
Packit ae235b
    {
Packit ae235b
      cmp->items = g_list_prepend (cmp->items, it->data);
Packit ae235b
      it = it->next;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_remove_items:
Packit ae235b
 * @cmp: the #GCompletion.
Packit ae235b
 * @items: (transfer none): the items to remove.
Packit ae235b
 *
Packit ae235b
 * Removes items from a #GCompletion. The items are not freed, so if the memory
Packit ae235b
 * was dynamically allocated, free @items with g_list_free_full() after calling
Packit ae235b
 * this function.
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
void 
Packit ae235b
g_completion_remove_items (GCompletion* cmp,
Packit ae235b
			   GList*	items)
Packit ae235b
{
Packit ae235b
  GList* it;
Packit ae235b
  
Packit ae235b
  g_return_if_fail (cmp != NULL);
Packit ae235b
  
Packit ae235b
  it = items;
Packit ae235b
  while (cmp->items && it)
Packit ae235b
    {
Packit ae235b
      cmp->items = g_list_remove (cmp->items, it->data);
Packit ae235b
      it = it->next;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  it = items;
Packit ae235b
  while (cmp->cache && it)
Packit ae235b
    {
Packit ae235b
      cmp->cache = g_list_remove(cmp->cache, it->data);
Packit ae235b
      it = it->next;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_clear_items:
Packit ae235b
 * @cmp: the #GCompletion.
Packit ae235b
 *
Packit ae235b
 * Removes all items from the #GCompletion. The items are not freed, so if the
Packit ae235b
 * memory was dynamically allocated, it should be freed after calling this
Packit ae235b
 * function.
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
void 
Packit ae235b
g_completion_clear_items (GCompletion* cmp)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (cmp != NULL);
Packit ae235b
  
Packit ae235b
  g_list_free (cmp->items);
Packit ae235b
  cmp->items = NULL;
Packit ae235b
  g_list_free (cmp->cache);
Packit ae235b
  cmp->cache = NULL;
Packit ae235b
  g_free (cmp->prefix);
Packit ae235b
  cmp->prefix = NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void   
Packit ae235b
completion_check_cache (GCompletion* cmp,
Packit ae235b
			gchar**	     new_prefix)
Packit ae235b
{
Packit ae235b
  GList* list;
Packit ae235b
  gsize len;
Packit ae235b
  gsize i;
Packit ae235b
  gsize plen;
Packit ae235b
  gchar* postfix;
Packit ae235b
  gchar* s;
Packit ae235b
  
Packit ae235b
  if (!new_prefix)
Packit ae235b
    return;
Packit ae235b
  if (!cmp->cache)
Packit ae235b
    {
Packit ae235b
      *new_prefix = NULL;
Packit ae235b
      return;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  len = strlen(cmp->prefix);
Packit ae235b
  list = cmp->cache;
Packit ae235b
  s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
Packit ae235b
  postfix = s + len;
Packit ae235b
  plen = strlen (postfix);
Packit ae235b
  list = list->next;
Packit ae235b
  
Packit ae235b
  while (list && plen)
Packit ae235b
    {
Packit ae235b
      s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
Packit ae235b
      s += len;
Packit ae235b
      for (i = 0; i < plen; ++i)
Packit ae235b
	{
Packit ae235b
	  if (postfix[i] != s[i])
Packit ae235b
	    break;
Packit ae235b
	}
Packit ae235b
      plen = i;
Packit ae235b
      list = list->next;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  *new_prefix = g_new0 (gchar, len + plen + 1);
Packit ae235b
  strncpy (*new_prefix, cmp->prefix, len);
Packit ae235b
  strncpy (*new_prefix + len, postfix, plen);
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_complete_utf8:
Packit ae235b
 * @cmp: the #GCompletion
Packit ae235b
 * @prefix: the prefix string, typically used by the user, which is compared
Packit ae235b
 *    with each of the items
Packit ae235b
 * @new_prefix: if non-%NULL, returns the longest prefix which is common to all
Packit ae235b
 *    items that matched @prefix, or %NULL if no items matched @prefix.
Packit ae235b
 *    This string should be freed when no longer needed.
Packit ae235b
 *
Packit ae235b
 * Attempts to complete the string @prefix using the #GCompletion target items.
Packit ae235b
 * In contrast to g_completion_complete(), this function returns the largest common
Packit ae235b
 * prefix that is a valid UTF-8 string, omitting a possible common partial 
Packit ae235b
 * character.
Packit ae235b
 *
Packit ae235b
 * You should use this function instead of g_completion_complete() if your 
Packit ae235b
 * items are UTF-8 strings.
Packit ae235b
 *
Packit ae235b
 * Returns: (element-type utf8) (transfer none): the list of items whose strings begin with @prefix. This should
Packit ae235b
 * not be changed.
Packit ae235b
 *
Packit ae235b
 * Since: 2.4
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
GList*
Packit ae235b
g_completion_complete_utf8 (GCompletion  *cmp,
Packit ae235b
			    const gchar  *prefix,
Packit ae235b
			    gchar       **new_prefix)
Packit ae235b
{
Packit ae235b
  GList *list;
Packit ae235b
  gchar *p, *q;
Packit ae235b
Packit ae235b
  list = g_completion_complete (cmp, prefix, new_prefix);
Packit ae235b
Packit ae235b
  if (new_prefix && *new_prefix)
Packit ae235b
    {
Packit ae235b
      p = *new_prefix + strlen (*new_prefix);
Packit ae235b
      q = g_utf8_find_prev_char (*new_prefix, p);
Packit ae235b
      
Packit ae235b
      switch (g_utf8_get_char_validated (q, p - q))
Packit ae235b
	{
Packit ae235b
	case (gunichar)-2:
Packit ae235b
	case (gunichar)-1:
Packit ae235b
	  *q = 0;
Packit ae235b
	  break;
Packit ae235b
	default: ;
Packit ae235b
	}
Packit ae235b
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return list;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_complete:
Packit ae235b
 * @cmp: the #GCompletion.
Packit ae235b
 * @prefix: the prefix string, typically typed by the user, which is
Packit ae235b
 *          compared with each of the items.
Packit ae235b
 * @new_prefix: if non-%NULL, returns the longest prefix which is
Packit ae235b
 *              common to all items that matched @prefix, or %NULL if
Packit ae235b
 *              no items matched @prefix.  This string should be freed
Packit ae235b
 *              when no longer needed.
Packit ae235b
 *
Packit ae235b
 * Attempts to complete the string @prefix using the #GCompletion
Packit ae235b
 * target items.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer none): the list of items whose strings begin with
Packit ae235b
 *          @prefix. This should not be changed.
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
GList* 
Packit ae235b
g_completion_complete (GCompletion* cmp,
Packit ae235b
		       const gchar* prefix,
Packit ae235b
		       gchar**	    new_prefix)
Packit ae235b
{
Packit ae235b
  gsize plen, len;
Packit ae235b
  gboolean done = FALSE;
Packit ae235b
  GList* list;
Packit ae235b
  
Packit ae235b
  g_return_val_if_fail (cmp != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (prefix != NULL, NULL);
Packit ae235b
  
Packit ae235b
  len = strlen (prefix);
Packit ae235b
  if (cmp->prefix && cmp->cache)
Packit ae235b
    {
Packit ae235b
      plen = strlen (cmp->prefix);
Packit ae235b
      if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen))
Packit ae235b
	{ 
Packit ae235b
	  /* use the cache */
Packit ae235b
	  list = cmp->cache;
Packit ae235b
	  while (list)
Packit ae235b
	    {
Packit ae235b
	      GList *next = list->next;
Packit ae235b
	      
Packit ae235b
	      if (cmp->strncmp_func (prefix,
Packit ae235b
				     cmp->func ? cmp->func (list->data) : (gchar*) list->data,
Packit ae235b
				     len))
Packit ae235b
		cmp->cache = g_list_delete_link (cmp->cache, list);
Packit ae235b
Packit ae235b
	      list = next;
Packit ae235b
	    }
Packit ae235b
	  done = TRUE;
Packit ae235b
	}
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  if (!done)
Packit ae235b
    {
Packit ae235b
      /* normal code */
Packit ae235b
      g_list_free (cmp->cache);
Packit ae235b
      cmp->cache = NULL;
Packit ae235b
      list = cmp->items;
Packit ae235b
      while (*prefix && list)
Packit ae235b
	{
Packit ae235b
	  if (!cmp->strncmp_func (prefix,
Packit ae235b
			cmp->func ? cmp->func (list->data) : (gchar*) list->data,
Packit ae235b
			len))
Packit ae235b
	    cmp->cache = g_list_prepend (cmp->cache, list->data);
Packit ae235b
	  list = list->next;
Packit ae235b
	}
Packit ae235b
    }
Packit ae235b
  if (cmp->prefix)
Packit ae235b
    {
Packit ae235b
      g_free (cmp->prefix);
Packit ae235b
      cmp->prefix = NULL;
Packit ae235b
    }
Packit ae235b
  if (cmp->cache)
Packit ae235b
    cmp->prefix = g_strdup (prefix);
Packit ae235b
  completion_check_cache (cmp, new_prefix);
Packit ae235b
  
Packit ae235b
  return *prefix ? cmp->cache : cmp->items;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_free:
Packit ae235b
 * @cmp: the #GCompletion.
Packit ae235b
 *
Packit ae235b
 * Frees all memory used by the #GCompletion. The items are not freed, so if
Packit ae235b
 * the memory was dynamically allocated, it should be freed after calling this
Packit ae235b
 * function.
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
void 
Packit ae235b
g_completion_free (GCompletion* cmp)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (cmp != NULL);
Packit ae235b
  
Packit ae235b
  g_completion_clear_items (cmp);
Packit ae235b
  g_free (cmp);
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_completion_set_compare:
Packit ae235b
 * @cmp: a #GCompletion.
Packit ae235b
 * @strncmp_func: the string comparison function.
Packit ae235b
 *
Packit ae235b
 * Sets the function to use for string comparisons. The default string
Packit ae235b
 * comparison function is strncmp().
Packit ae235b
 *
Packit ae235b
 * Deprecated: 2.26: Rarely used API
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
g_completion_set_compare(GCompletion *cmp,
Packit ae235b
			 GCompletionStrncmpFunc strncmp_func)
Packit ae235b
{
Packit ae235b
  cmp->strncmp_func = strncmp_func;
Packit ae235b
}
Packit ae235b
Packit ae235b
#ifdef TEST_COMPLETION
Packit ae235b
#include <stdio.h>
Packit ae235b
int
Packit ae235b
main (int   argc,
Packit ae235b
      char* argv[])
Packit ae235b
{
Packit ae235b
  FILE *file;
Packit ae235b
  gchar buf[1024];
Packit ae235b
  GList *list;
Packit ae235b
  GList *result;
Packit ae235b
  GList *tmp;
Packit ae235b
  GCompletion *cmp;
Packit ae235b
  gint i;
Packit ae235b
  gchar *longp = NULL;
Packit ae235b
  
Packit ae235b
  if (argc < 3)
Packit ae235b
    {
Packit ae235b
      g_warning ("Usage: %s filename prefix1 [prefix2 ...]\n", argv[0]);
Packit ae235b
      return 1;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  file = fopen (argv[1], "r");
Packit ae235b
  if (!file)
Packit ae235b
    {
Packit ae235b
      g_warning ("Cannot open %s\n", argv[1]);
Packit ae235b
      return 1;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  cmp = g_completion_new (NULL);
Packit ae235b
  list = g_list_alloc ();
Packit ae235b
  while (fgets (buf, 1024, file))
Packit ae235b
    {
Packit ae235b
      list->data = g_strdup (buf);
Packit ae235b
      g_completion_add_items (cmp, list);
Packit ae235b
    }
Packit ae235b
  fclose (file);
Packit ae235b
  
Packit ae235b
  for (i = 2; i < argc; ++i)
Packit ae235b
    {
Packit ae235b
      printf ("COMPLETING: %s\n", argv[i]);
Packit ae235b
      result = g_completion_complete (cmp, argv[i], &longp;;
Packit ae235b
      g_list_foreach (result, (GFunc) printf, NULL);
Packit ae235b
      printf ("LONG MATCH: %s\n", longp);
Packit ae235b
      g_free (longp);
Packit ae235b
      longp = NULL;
Packit ae235b
    }
Packit ae235b
  
Packit ae235b
  g_list_foreach (cmp->items, (GFunc) g_free, NULL);
Packit ae235b
  g_completion_free (cmp);
Packit ae235b
  g_list_free (list);
Packit ae235b
  
Packit ae235b
  return 0;
Packit ae235b
}
Packit ae235b
#endif