Esempio n. 1
0
static void
on_get_surrounding_text (MInputContext *context,
                         MSymbol        command)
{
  g_debug (G_STRLOC ": %s", G_STRFUNC);

  NimfM17n *m17n = context->arg;

  if (!NIMF_IS_M17N (m17n) || !nimf_service_im_target)
    return;

  gchar *text;
  gint   cursor_pos, nchars, nbytes;
  MText *mt, *surround = NULL;
  int    len, pos;

  nimf_engine_get_surrounding (NIMF_ENGINE (m17n),
                               nimf_service_im_target,
                               &text,
                               &cursor_pos);
  if (text == NULL)
    return;

  nchars = g_utf8_strlen (text, -1);
  nbytes = strlen (text);

  mt = mconv_decode_buffer (Mcoding_utf_8,
                            (const unsigned char *) text, nbytes);
  g_free (text);

  len = (long) mplist_value (m17n->ic->plist);

  if (len < 0)
  {
    pos = cursor_pos + len;

    if (pos < 0)
      pos = 0;

    surround = mtext_duplicate (mt, pos, cursor_pos);
  }
  else if (len > 0)
  {
    pos = cursor_pos + len;

    if (pos > nchars)
      pos = nchars;

    surround = mtext_duplicate (mt, cursor_pos, pos);
  }

  if (!surround)
    surround = mtext ();

  m17n_object_unref (mt);
  mplist_set (m17n->ic->plist, Mtext, surround);
  m17n_object_unref (surround);
}
Esempio n. 2
0
static gboolean
ibus_m17n_engine_process_key (IBusM17NEngine *m17n,
                              MSymbol         key)
{
    gchar *buf;
    MText *produced;
    gint retval;

    retval = minput_filter (m17n->context, key, NULL);

    if (retval) {
        return TRUE;
    }

    produced = mtext ();

    retval = minput_lookup (m17n->context, key, NULL, produced);

    if (retval) {
        // g_debug ("minput_lookup returns %d", retval);
    }

    buf = ibus_m17n_mtext_to_utf8 (produced);
    m17n_object_unref (produced);

    if (buf && strlen (buf)) {
        ibus_m17n_engine_commit_string (m17n, buf);
    }
    g_free (buf);

    return retval == 0;
}
Esempio n. 3
0
File: m17nlib.c Progetto: NgoHuy/uim
static uim_lisp
get_input_method_short_desc(uim_lisp nth_)
{
  int nth;
  char *str = NULL, *p;
  uim_lisp ret;

  nth = C_INT(nth_);

  if (nth < nr_input_methods) {
    MInputMethod *im;
    MText *desc;

    im = im_instance(nth);
    if (!im)
      return MAKE_STR(N_("m17n library IM open error"));

    desc = minput_get_description(im->language, im->name);
    if (desc) {
      int i, len;

      str = convert_mtext2str(desc);
      p = strchr(str, '.');
      if (p)
	*p = '\0';
      len = strlen(str);

      /*
       * Workaround for the descriptions which lack period.
       * Also we avoid the description with non English words.
       * See https://bugs.freedesktop.org/show_bug.cgi?id=6972
       */
      for (i = 0; i < len; i++) {
	if (str[i] == '\n') {
	  str[i] = '\0';
	  break;
	}
#ifdef HAVE_ISASCII
	else if (!isascii((int)str[i])) {
#else
	else if ((int)str[i] & ~0x7f) {
#endif
	  free(str);
	  str = NULL;
	  break;
	}
      }
      m17n_object_unref(desc);
    }

    if (str) {
      ret = MAKE_STR(str);
      free(str);
    } else {
      ret = MAKE_STR(N_("An input method provided by the m17n library"));
    }
  } else
Esempio n. 4
0
static MPlist *
add_action (MPlist *actions, MSymbol name, MSymbol key, void *val)
{
  MPlist *action = mplist ();

  mplist_add (action, Msymbol, name);
  if (key != Mnil)
    mplist_add (action, key, val);
  mplist_add (actions, Mplist, action);
  m17n_object_unref (action);
  return actions;
}
Esempio n. 5
0
static MPlist *
paginate (MPlist *candidates)
{
  MPlist *p = candidates, *pl = mplist (), *plist = mplist ();
  int i;

  for (i = 0; mplist_key (p) == Mtext; p = mplist_next (p), i++)
    {
      mplist_add (pl, Mtext, mplist_value (p));
      if (i % 10 == 9)
	{
	  mplist_add (plist, Mplist, pl);
	  m17n_object_unref (pl);
	  pl = mplist ();
	}
    }

  if (mplist_key (pl) != Mnil)
    mplist_add (plist, Mplist, pl);
  m17n_object_unref (pl);
  return plist;
}
Esempio n. 6
0
int m17n_ccx_freeconv(M17nCCX *m17n){
	if( m17n->m_mtext ){
		m17n_object_unref(m17n->m_mtext);
		m17n->m_mtext = 0;
	}
	if( m17n->m_dec ){
		mconv_free_converter(m17n->m_dec);
		m17n->m_dec = 0;
	}
	if( m17n->m_enc ){
		mconv_free_converter(m17n->m_enc);
		m17n->m_enc = 0;
	}
	return 0;
}
Esempio n. 7
0
File: m17nlib.c Progetto: NgoHuy/uim
static uim_lisp
init_m17nlib()
{
  MPlist *imlist, *elm;

  M17N_INIT();
  nr_input_methods = 0;
  nr_input_contexts = 0;
  im_array = NULL;
  ic_array = NULL;

  imlist = mdatabase_list(msymbol("input-method"), Mnil, Mnil, Mnil);

  if (!imlist) {
    /* maybe user forgot to install m17n-db */
    return uim_scm_f();
  }

  for (elm = imlist; mplist_key(elm) != Mnil; elm = mplist_next(elm)) {
    MDatabase *mdb;
    MSymbol *tag, lang, imname;
    uim_bool is_complete_im;

    mdb = mplist_value(elm);
    tag = mdatabase_tag(mdb);
    lang = tag[1];
    imname = tag[2];
    is_complete_im = (lang != Mnil && imname != Mnil);  /* [uim-ja 30] */

    if (is_complete_im) {
      /* pass NULL as IM to enable lazy instantiation */
      pushback_input_method(NULL, msymbol_name(lang), msymbol_name(imname));
    }
  }
#if 0
  register_callbacks();
#endif
  m17n_object_unref(imlist);
  converter = mconv_buffer_converter(msymbol("utf8"), NULL, 0);

  if (!converter)
    return uim_scm_f();

  m17nlib_ok = 1;

  return uim_scm_t();
}
Esempio n. 8
0
gboolean
nimf_m17n_filter_event (NimfEngine    *engine,
                        NimfServiceIC *target,
                        NimfEvent     *event)
{
  g_debug (G_STRLOC ": %s", G_STRFUNC);

  NimfM17n *m17n = NIMF_M17N (engine);

  g_return_val_if_fail (m17n->im != NULL, FALSE);

  if (event->key.type   == NIMF_EVENT_KEY_RELEASE ||
      event->key.keyval == NIMF_KEY_Shift_L       ||
      event->key.keyval == NIMF_KEY_Shift_R)
    return FALSE;

  nimf_service_im_target = target;
  guint keyval = event->key.keyval;
  gboolean move = FALSE;

  if (nimf_candidatable_is_visible (m17n->candidatable))
  {
    switch (keyval)
    {
      case NIMF_KEY_Up:
      case NIMF_KEY_KP_Up:
        keyval = NIMF_KEY_Left;
        break;
      case NIMF_KEY_Down:
      case NIMF_KEY_KP_Down:
        keyval = NIMF_KEY_Right;
        break;
      case NIMF_KEY_Left:
      case NIMF_KEY_KP_Left:
      case NIMF_KEY_Page_Up:
      case NIMF_KEY_KP_Page_Up:
        keyval = NIMF_KEY_Up;
        nimf_m17n_page_up (m17n);
        break;
      case NIMF_KEY_Right:
      case NIMF_KEY_KP_Right:
      case NIMF_KEY_Page_Down:
      case NIMF_KEY_KP_Page_Down:
        keyval = NIMF_KEY_Down;
        nimf_m17n_page_down (m17n);
        break;
      case NIMF_KEY_KP_0:
        keyval = NIMF_KEY_0;
        break;
      case NIMF_KEY_KP_1:
        keyval = NIMF_KEY_1;
        break;
      case NIMF_KEY_KP_2:
        keyval = NIMF_KEY_2;
        break;
      case NIMF_KEY_KP_3:
        keyval = NIMF_KEY_3;
        break;
      case NIMF_KEY_KP_4:
        keyval = NIMF_KEY_4;
        break;
      case NIMF_KEY_KP_5:
        keyval = NIMF_KEY_5;
        break;
      case NIMF_KEY_KP_6:
        keyval = NIMF_KEY_6;
        break;
      case NIMF_KEY_KP_7:
        keyval = NIMF_KEY_7;
        break;
      case NIMF_KEY_KP_8:
        keyval = NIMF_KEY_8;
        break;
      case NIMF_KEY_KP_9:
        keyval = NIMF_KEY_9;
        break;
      default:
        move = TRUE;
        break;
    }
  }

  const gchar *keysym_name;
  gboolean retval;

  keysym_name = nimf_keyval_to_keysym_name (keyval);
  MSymbol symbol;

  if (keysym_name)
  {
    GString *string;
    string = g_string_new ("");

    if (event->key.state & NIMF_HYPER_MASK)
      g_string_append (string, "H-");

    if (event->key.state & NIMF_SUPER_MASK)
      g_string_append (string, "s-");

    if (event->key.state & NIMF_MOD5_MASK)
      g_string_append (string, "G-");

    if (event->key.state & NIMF_MOD1_MASK)
      g_string_append (string, "A-");

    if (event->key.state & NIMF_META_MASK)
      g_string_append (string, "M-");

    if (event->key.state & NIMF_CONTROL_MASK)
      g_string_append (string, "C-");

    if (event->key.state & NIMF_SHIFT_MASK)
      g_string_append (string, "S-");

    g_string_append (string, keysym_name);
    symbol = msymbol (string->str);
    g_string_free (string, TRUE);
  }
  else
  {
    g_warning (G_STRLOC ": %s: keysym name not found", G_STRFUNC);
    symbol = Mnil;
  }

  retval = minput_filter (m17n->ic, symbol, NULL);

  if (!retval)
  {
    MText *produced;
    produced = mtext ();
    retval = !minput_lookup (m17n->ic, symbol, NULL, produced);

    if (mtext_len (produced) > 0)
    {
      gchar *buf;
      buf = nimf_m17n_mtext_to_utf8 (m17n, produced);

      if (m17n->converter->nbytes > 0)
        nimf_engine_emit_commit (engine, target, (const gchar *) buf);

      g_free (buf);
    }

    m17n_object_unref (produced);
  }

  if (m17n->ic->preedit_changed)
  {
    gchar *new_preedit = nimf_m17n_mtext_to_utf8 (m17n, m17n->ic->preedit);
    nimf_m17n_update_preedit (engine, target, new_preedit);
  }

  if (m17n->ic->status_changed)
  {
    gchar *status;
    status = nimf_m17n_mtext_to_utf8 (m17n, m17n->ic->status);

    if (status && strlen (status))
      g_debug ("Minput_status_draw: %s", status);

    g_free (status);
  }

  if (m17n->ic->candidate_list && m17n->ic->candidate_show)
  {
    nimf_m17n_update_candidate (engine, target);

    if (!nimf_candidatable_is_visible (m17n->candidatable))
      nimf_candidatable_show (m17n->candidatable, target, FALSE);
    else if (move)
      nimf_candidatable_show (m17n->candidatable, target, FALSE);
  }
  else
  {
    nimf_candidatable_hide (m17n->candidatable);
  }

  nimf_service_im_target = NULL;

  return retval;
}
Esempio n. 9
0
MPlist *
lookup (MPlist *args)
{
  MInputContext *ic;
  MPlist *actions = NULL, *candidates, *plist;
  MSymbol init_state;
  MSymbol select_state;
  TableContext *context;
  MText *mt;

  ic = mplist_value (args);
  context = get_context (ic);

  args = mplist_next (args);
  init_state = (MSymbol) mplist_value (args);
  args = mplist_next (args);
  select_state = (MSymbol) mplist_value (args);

  if (context->desc)
    candidates = (*context->desc->lookup) (context, args);
  else
    candidates = mplist ();

  if (mplist_length (candidates) == 0) {
    m17n_object_unref (candidates);
    return NULL;
  }

#if 0
  /* FIXME: if only one candidate is matching, we should insert it and
     commit immediately.  However, this feature is disabled for now
     since users would type extra characters after the match.  For
     example, with mr-inscript-typing-booster, an Indic word
     "Epgyepgne" is only one candidate when a user type "Epgyepgn",
     but the user will likely to type "e" after commit.
  */
  if (mplist_length (candidates) == 1) {
    actions = mplist ();
    add_action (actions, msymbol ("delete"), Msymbol,  msymbol ("@<"));
    add_action (actions, msymbol ("insert"), Mtext, mplist_value (candidates));
    add_action (actions, msymbol ("commit"), Mnil, NULL);
    m17n_object_unref (candidates);
    return actions;
  }
#endif

  actions = mplist ();

  mt = mtext_dup (ic->preedit);
  mplist_push (candidates, Mtext, mt);
  m17n_object_unref (mt);
  plist = paginate (candidates);
  m17n_object_unref (candidates);

  add_action (actions, msymbol ("delete"), Msymbol,  msymbol ("@<"));
  mplist_add (actions, Mplist, plist);
  m17n_object_unref (plist);
  add_action (actions, msymbol ("select"), Minteger, (void *)1);
  add_action (actions, msymbol ("show"), Mnil, NULL);
  add_action (actions, msymbol ("shift"), Msymbol, select_state);

  return actions;
}
Esempio n. 10
0
static MPlist *
lookup_scim (TableContext *context, MPlist *args)
{
  unsigned char buf[256];
  char *word = NULL;
  MPlist *candidates = mplist ();
  MText *mt;
  int rc, len, xlen;
  TablePhrase *phrases;
  int n_phrases, n_allocated_phrases;

  if (!context->content || !context->offsets)
    goto out;

  rc = mtext_to_utf8 (context, context->ic->preedit, buf, sizeof (buf));
  if (rc < 0 || rc >= context->mlen)
    goto out;
  word = strdup ((const char *)buf);
  len = rc;

  n_allocated_phrases = 2;
  phrases = calloc (sizeof (TablePhrase), n_allocated_phrases);
  for (n_phrases = 0, xlen = context->xlen; xlen <= context->mlen
	 && n_phrases == 0; xlen++)
    {
      int j;

      for (j = xlen; j > 0; j--)
	{
	  TableOffsetArray *array = &context->offsets[j - 1];
	  int i;

	  for (i = 0; i < array->len; i++)
	    {
	      unsigned char *data = &context->content[array->data[i]];
	      int klen = *data & 0x3F;
	      int plen = *(data + 1);

	      if (strncmp ((const char *)(data + 4), word, len) == 0)
		{
		  int freq = scim_bytestouint16 (data + 2);

		  if (++n_phrases > n_allocated_phrases)
		    {
		      n_allocated_phrases *= 2;
		      phrases = realloc (phrases,
					 sizeof (TablePhrase)
					 * n_allocated_phrases);
		    }
		  phrases[n_phrases - 1].text =
		    strndup ((const char *)(data + 4 + klen), plen);
		  phrases[n_phrases - 1].freq = freq;
		}
	    }
	}
    }

  qsort (phrases, n_phrases, sizeof (TablePhrase), cmp_phrases_by_freq);
  while (n_phrases--)
    {
      mt = mtext_from_utf8 (context,
			    (const unsigned char *)phrases[n_phrases].text,
			    strlen (phrases[n_phrases].text));
      free (phrases[n_phrases].text);

      if (!context->max_candidates
	  || mplist_length (candidates) < context->max_candidates)
	{
	  mplist_push (candidates, Mtext, mt);
#ifdef DEBUG
	  mdebug_dump_mtext (mt, 0, 0);
#endif
	  m17n_object_unref (mt);
	}
    }
  free (phrases);

 out:
  if (word)
    free (word);

  return candidates;
}
Esempio n. 11
0
static MPlist *
lookup_ibus (TableContext *context, MPlist *args)
{
  unsigned char buf[256];
  char *word = NULL, *sql = NULL, *msql = NULL;
  MPlist *candidates = mplist ();
  int len, xlen, wlen, mlen;
  int i, rc;
  int *m = NULL;
  sqlite3_stmt *stmt;
  MText *mt;

  if (!context->db)
    goto out;

  rc = mtext_to_utf8 (context, context->ic->preedit, buf, sizeof (buf));
  if (rc < 0)
    goto out;
  word = strdup ((const char *)buf);
  len = rc;

  mlen = CLAMP(context->mlen, 0, 99);

  rc = encode_phrase ((const unsigned char *)word, &m);
  if (rc)
    goto out;

  /* strlen(" AND mXX = XX") = 13 */
  if (len > mlen)
    len = mlen;
  msql = calloc (sizeof (char), 13 * len + 1);
  if (!msql)
    goto out;
  for (i = 0; i < len; i++)
    {
      char s[14];
      sqlite3_snprintf (13, s, " AND m%d = %d", i, m[i]);
      strcat (msql, s);
    }

  sql = calloc (sizeof (char), 128 + strlen (msql) + 1);
  if (!sql)
    goto out;

  /* issue query repeatedly until at least one candidates are found or
     the key length is exceeds mlen */
  xlen = context->xlen;
  wlen = mlen - len + 1;
  for (; xlen <= wlen + 1; xlen++)
    {
      sqlite3_snprintf (128,
			sql,
			"SELECT id, phrase FROM phrases WHERE mlen < %lu",
			len + xlen);
      strcat (sql, msql);
      strcat (sql, " ORDER BY mlen ASC, user_freq DESC, freq DESC, id ASC");
      if (context->max_candidates)
	sqlite3_snprintf (128,
			  sql + strlen (sql),
			  " LIMIT %lu",
			  context->max_candidates);

#ifdef DEBUG
      fprintf (stderr, "%s\n", sql);
#endif
      rc = sqlite3_prepare (context->db, sql, strlen (sql), &stmt, NULL);
      if (rc != SQLITE_OK)
	{
	  sqlite3_finalize (stmt);
	  goto out;
	}

      while (sqlite3_step (stmt) == SQLITE_ROW)
	{
	  const unsigned char *text;

	  text = sqlite3_column_text (stmt, 1);
#ifdef DEBUG
	  fprintf (stderr, " %s\n", text);
#endif
	  mt = mtext_from_utf8 (context, text, strlen ((const char *)text));
	  mplist_add (candidates, Mtext, mt);
	  m17n_object_unref (mt);
	}
      sqlite3_finalize (stmt);
      if (mplist_length (candidates) > 0)
	break;
    }

 out:
  if (word)
    free (word);
  if (m)
    free (m);
  if (sql)
    free (sql);
  if (msql)
    free (msql);

  return candidates;
}
Esempio n. 12
0
static void
ibus_m17n_engine_callback (MInputContext *context,
                           MSymbol        command)
{
    IBusM17NEngine *m17n = NULL;

    m17n = context->arg;
    g_return_if_fail (m17n != NULL);

    /* the callback may be called in minput_create_ic, in the time
     * m17n->context has not be assigned, so need assign it. */
    if (m17n->context == NULL) {
        m17n->context = context;
    }

    if (command == Minput_preedit_start) {
        ibus_engine_hide_preedit_text ((IBusEngine *)m17n);
    }
    else if (command == Minput_preedit_draw) {
        ibus_m17n_engine_update_preedit (m17n);
    }
    else if (command == Minput_preedit_done) {
        ibus_engine_hide_preedit_text ((IBusEngine *)m17n);
    }
    else if (command == Minput_status_start) {
        ibus_engine_hide_preedit_text ((IBusEngine *)m17n);
    }
    else if (command == Minput_status_draw) {
        gchar *status;
        status = ibus_m17n_mtext_to_utf8 (m17n->context->status);

        if (status && strlen (status)) {
            IBusText *text;
            text = ibus_text_new_from_string (status);
            ibus_property_set_label (m17n->status_prop, text);
            ibus_property_set_visible (m17n->status_prop, TRUE);
        }
        else {
            ibus_property_set_label (m17n->status_prop, NULL);
            ibus_property_set_visible (m17n->status_prop, FALSE);
        }

        ibus_engine_update_property ((IBusEngine *)m17n, m17n->status_prop);
        g_free (status);
    }
    else if (command == Minput_status_done) {
    }
    else if (command == Minput_candidates_start) {
        ibus_engine_hide_lookup_table ((IBusEngine *) m17n);
        ibus_engine_hide_auxiliary_text ((IBusEngine *) m17n);
    }
    else if (command == Minput_candidates_draw) {
        ibus_m17n_engine_update_lookup_table (m17n);
    }
    else if (command == Minput_candidates_done) {
        ibus_engine_hide_lookup_table ((IBusEngine *) m17n);
        ibus_engine_hide_auxiliary_text ((IBusEngine *) m17n);
    }
    else if (command == Minput_set_spot) {
    }
    else if (command == Minput_toggle) {
    }
    else if (command == Minput_reset) {
    }
    /* ibus_engine_get_surrounding_text is only available in the current
       git master (1.3.99+) */
#ifdef HAVE_IBUS_ENGINE_GET_SURROUNDING_TEXT
    else if (command == Minput_get_surrounding_text &&
             (((IBusEngine *) m17n)->client_capabilities &
              IBUS_CAP_SURROUNDING_TEXT) != 0) {
        IBusText *text;
        guint cursor_pos, nchars, nbytes;
        MText *mt, *surround;
        int len, pos;

        ibus_engine_get_surrounding_text ((IBusEngine *) m17n,
                                          &text,
                                          &cursor_pos);
        nchars = ibus_text_get_length (text);
        nbytes = g_utf8_offset_to_pointer (text->text, nchars) - text->text;
        mt = mconv_decode_buffer (Mcoding_utf_8, text->text, nbytes);
        g_object_unref (text);

        len = (long) mplist_value (m17n->context->plist);
        if (len < 0) {
            pos = cursor_pos + len;
            if (pos < 0)
                pos = 0;
            surround = mtext_duplicate (mt, pos, cursor_pos);
        }
        else if (len > 0) {
            pos = cursor_pos + len;
            if (pos > nchars)
                pos = nchars;
            surround = mtext_duplicate (mt, cursor_pos, pos);
        }
        else {
            surround = mtext ();
        }
        m17n_object_unref (mt);
        mplist_set (m17n->context->plist, Mtext, surround);
        m17n_object_unref (surround);
    }
#endif  /* !HAVE_IBUS_ENGINE_GET_SURROUNDING_TEXT */
    else if (command == Minput_delete_surrounding_text &&
             (((IBusEngine *) m17n)->client_capabilities &
              IBUS_CAP_SURROUNDING_TEXT) != 0) {
        int len;

        len = (long) mplist_value (m17n->context->plist);
        if (len < 0)
            ibus_engine_delete_surrounding_text ((IBusEngine *) m17n,
                                                 len, -len);
        else if (len > 0)
            ibus_engine_delete_surrounding_text ((IBusEngine *) m17n,
                                                 0, len);
    }
}
Esempio n. 13
0
static gchar *
transliterator_m17n_real_transliterate (TranslitTransliterator *self,
                                        const gchar            *input,
                                        guint                  *endpos,
                                        GError                **error)
{
  TransliteratorM17n *m17n = TRANSLITERATOR_M17N (self);
  const gchar *p;
  GString *string;
  gchar *output;
  gint n_filtered = 0;

  string = g_string_sized_new (strlen (input));
  minput_reset_ic (m17n->ic);
  for (p = input; ; p = g_utf8_next_char (p))
    {
      gunichar uc = g_utf8_get_char (p);
      MSymbol symbol;
      gint retval;

      if (*p == '\0')
	symbol = Mnil;
      else
	{
	  gint length;
	  gchar *utf8;

	  length = g_unichar_to_utf8 (uc, NULL);
	  utf8 = g_slice_alloc0 (length + 1);
	  g_unichar_to_utf8 (uc, utf8);
	  symbol = msymbol (utf8);
	  g_slice_free1 (length, utf8);
	}

      retval = minput_filter (m17n->ic, symbol, NULL);
      if (retval == 0)
	{
	  MText *mt = mtext ();

	  retval = minput_lookup (m17n->ic, symbol, NULL, mt);

	  if (mtext_len (mt) > 0) {
	    output = mtext_to_utf8 (mt);
	    g_string_append (string, output);
	    g_free (output);
	  }

	  if (retval)
	    g_string_append_unichar (string, uc);

	  m17n_object_unref (mt);
	  n_filtered = 0;
	}
      else
	n_filtered++;

      if (symbol == Mnil)
	break;
    }

  if (endpos)
    *endpos = g_utf8_strlen (input, -1) - n_filtered;

  return g_string_free (string, FALSE);
}