Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  unaccent.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * unaccent.c
 *   Text search unaccent dictionary
 *
 * Copyright (c) 2009-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   contrib/unaccent/unaccent.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "catalog/pg_ts_dict.h"
#include "commands/defrem.h"
#include "lib/stringinfo.h"
#include "tsearch/ts_cache.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"

PG_MODULE_MAGIC_EXT(
     .name = "unaccent",
     .version = PG_VERSION
);

/*
 * An unaccent dictionary uses a trie to find a string to replace.  Each node
 * of the trie is an array of 256 TrieChar structs; the N-th element of the
 * array corresponds to next byte value N.  That element can contain both a
 * replacement string (to be used if the source string ends with this byte)
 * and a link to another trie node (to be followed if there are more bytes).
 *
 * Note that the trie search logic pays no attention to multibyte character
 * boundaries.  This is OK as long as both the data entered into the trie and
 * the data we're trying to look up are validly encoded; no partial-character
 * matches will occur.
 */

typedef struct TrieChar
{
 struct TrieChar *nextChar;
 char    *replaceTo;
 int   replacelen;
} TrieChar;

/*
 * placeChar - put str into trie's structure, byte by byte.
 *
 * If node is NULL, we need to make a new node, which will be returned;
 * otherwise the return value is the same as node.
 */

static TrieChar *
placeChar(TrieChar *node, const unsigned char *str, int lenstr,
    const char *replaceTo, int replacelen)
{
 TrieChar   *curnode;

 if (!node)
  node = (TrieChar *) palloc0(sizeof(TrieChar) * 256);

 Assert(lenstr > 0);   /* else str[0] doesn't exist */

 curnode = node + *str;

 if (lenstr <= 1)
 {
  if (curnode->replaceTo)
   ereport(WARNING,
     (errcode(ERRCODE_CONFIG_FILE_ERROR),
      errmsg("duplicate source strings, first one will be used")));
  else
  {
   curnode->replacelen = replacelen;
   curnode->replaceTo = (char *) palloc(replacelen);
   memcpy(curnode->replaceTo, replaceTo, replacelen);
  }
 }
 else
 {
  curnode->nextChar = placeChar(curnode->nextChar, str + 1, lenstr - 1,
           replaceTo, replacelen);
 }

 return node;
}

/*
 * initTrie  - create trie from file.
 *
 * Function converts UTF8-encoded file into current encoding.
 */

static TrieChar *
initTrie(const char *filename)
{
 TrieChar   *volatile rootTrie = NULL;
 MemoryContext ccxt = CurrentMemoryContext;
 tsearch_readline_state trst;
 volatile bool skip;

 filename = get_tsearch_config_filename(filename, "rules");
 if (!tsearch_readline_begin(&trst, filename))
  ereport(ERROR,
    (errcode(ERRCODE_CONFIG_FILE_ERROR),
     errmsg("could not open unaccent file \"%s\": %m",
      filename)));

 do
 {
  /*
   * pg_do_encoding_conversion() (called by tsearch_readline()) will
   * emit exception if it finds untranslatable characters in current
   * locale. We just skip such lines, continuing with the next.
 */

  skip = true;

  PG_TRY();
  {
   char    *line;

   while ((line = tsearch_readline(&trst)) != NULL)
   {
    /*----------
     * The format of each line must be "src" or "src trg", where
     * src and trg are sequences of one or more non-whitespace
     * characters, separated by whitespace.  Whitespace at start
     * or end of line is ignored.  If trg is omitted, an empty
     * string is used as the replacement.  trg can be optionally
     * quoted, in which case whitespaces are included in it.
     *
     * We use a simple state machine, with states
     * 0 initial (before src)
     * 1 in src
     * 2 in whitespace after src
     * 3 in trg (non-quoted)
     * 4 in trg (quoted)
     * 5 in whitespace after trg
     * -1 syntax error detected (two strings)
     * -2 syntax error detected (unfinished quoted string)
     *----------
 */

    int   state;
    char    *ptr;
    char    *src = NULL;
    char    *trg = NULL;
    char    *trgstore = NULL;
    int   ptrlen;
    int   srclen = 0;
    int   trglen = 0;
    int   trgstorelen = 0;
    bool  trgquoted = false;

    state = 0;
    for (ptr = line; *ptr; ptr += ptrlen)
    {
     ptrlen = pg_mblen_cstr(ptr);
     /* ignore whitespace, but end src or trg */
     if (isspace((unsigned char) *ptr))
     {
      if (state == 1)
       state = 2;
      else if (state == 3)
       state = 5;
      /* whitespaces are OK in quoted area */
      if (state != 4)
       continue;
     }
     switch (state)
     {
      case 0:
       /* start of src */
       src = ptr;
       srclen = ptrlen;
       state = 1;
       break;
      case 1:
       /* continue src */
       srclen += ptrlen;
       break;
      case 2:
       /* start of trg */
       if (*ptr == '"')
       {
        trgquoted = true;
        state = 4;
       }
       else
        state = 3;

       trg = ptr;
       trglen = ptrlen;
       break;
      case 3:
       /* continue non-quoted trg */
       trglen += ptrlen;
       break;
      case 4:
       /* continue quoted trg */
       trglen += ptrlen;

       /*
        * If this is a quote, consider it as the end of
        * trg except if the follow-up character is itself
        * a quote.
 */

       if (*ptr == '"')
       {
        if (*(ptr + 1) == '"')
        {
         ptr++;
         trglen += 1;
        }
        else
         state = 5;
       }
       break;
      default:
       /* bogus line format */
       state = -1;
       break;
     }
    }

    if (state == 1 || state == 2)
    {
     /* trg was omitted, so use "" */
     trg = "";
     trglen = 0;
    }

    /* If still in a quoted area, fallback to an error */
    if (state == 4)
     state = -2;

    /* If trg was quoted, remove its quotes and unescape it */
    if (trgquoted && state > 0)
    {
     /* Ignore first and end quotes */
     trgstore = (char *) palloc(sizeof(char) * (trglen - 2));
     trgstorelen = 0;
     for (int i = 1; i < trglen - 1; i++)
     {
      trgstore[trgstorelen] = trg[i];
      trgstorelen++;
      /* skip second double quotes */
      if (trg[i] == '"' && trg[i + 1] == '"')
       i++;
     }
    }
    else
    {
     trgstore = (char *) palloc(sizeof(char) * trglen);
     trgstorelen = trglen;
     memcpy(trgstore, trg, trgstorelen);
    }

    if (state > 0)
     rootTrie = placeChar(rootTrie,
           (unsigned char *) src, srclen,
           trgstore, trgstorelen);
    else if (state == -1)
     ereport(WARNING,
       (errcode(ERRCODE_CONFIG_FILE_ERROR),
        errmsg("invalid syntax: more than two strings in unaccent rule")));
    else if (state == -2)
     ereport(WARNING,
       (errcode(ERRCODE_CONFIG_FILE_ERROR),
        errmsg("invalid syntax: unfinished quoted string in unaccent rule")));

    pfree(trgstore);
    pfree(line);
   }
   skip = false;
  }
  PG_CATCH();
  {
   ErrorData  *errdata;
   MemoryContext ecxt;

   ecxt = MemoryContextSwitchTo(ccxt);
   errdata = CopyErrorData();
   if (errdata->sqlerrcode == ERRCODE_UNTRANSLATABLE_CHARACTER)
   {
    FlushErrorState();
   }
   else
   {
    MemoryContextSwitchTo(ecxt);
    PG_RE_THROW();
   }
  }
  PG_END_TRY();
 }
 while (skip);

 tsearch_readline_end(&trst);

 return rootTrie;
}

/*
 * findReplaceTo - find longest possible match in trie
 *
 * On success, returns pointer to ending subnode, plus length of matched
 * source string in *p_matchlen.  On failure, returns NULL.
 */

static TrieChar *
findReplaceTo(TrieChar *node, const unsigned char *src, int srclen,
     int *p_matchlen)
{
 TrieChar   *result = NULL;
 int   matchlen = 0;

 *p_matchlen = 0;   /* prevent uninitialized-variable warnings */

 while (node && matchlen < srclen)
 {
  node = node + src[matchlen];
  matchlen++;

  if (node->replaceTo)
  {
   result = node;
   *p_matchlen = matchlen;
  }

  node = node->nextChar;
 }

 return result;
}

PG_FUNCTION_INFO_V1(unaccent_init);
Datum
unaccent_init(PG_FUNCTION_ARGS)
{
 List    *dictoptions = (List *) PG_GETARG_POINTER(0);
 TrieChar   *rootTrie = NULL;
 bool  fileloaded = false;
 ListCell   *l;

 foreach(l, dictoptions)
 {
  DefElem    *defel = (DefElem *) lfirst(l);

  if (strcmp(defel->defname, "rules") == 0)
  {
   if (fileloaded)
    ereport(ERROR,
      (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
       errmsg("multiple Rules parameters")));
   rootTrie = initTrie(defGetString(defel));
   fileloaded = true;
  }
  else
  {
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("unrecognized Unaccent parameter: \"%s\"",
       defel->defname)));
  }
 }

 if (!fileloaded)
 {
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     errmsg("missing Rules parameter")));
 }

 PG_RETURN_POINTER(rootTrie);
}

PG_FUNCTION_INFO_V1(unaccent_lexize);
Datum
unaccent_lexize(PG_FUNCTION_ARGS)
{
 TrieChar   *rootTrie = (TrieChar *) PG_GETARG_POINTER(0);
 char    *srcchar = (char *) PG_GETARG_POINTER(1);
 int32  len = PG_GETARG_INT32(2);
 char    *srcstart = srcchar;
 const char *srcend = srcstart + len;
 TSLexeme   *res;
 StringInfoData buf;

 /* we allocate storage for the buffer only if needed */
 buf.data = NULL;

 while (len > 0)
 {
  TrieChar   *node;
  int   matchlen;

  node = findReplaceTo(rootTrie, (unsigned char *) srcchar, len,
        &matchlen);
  if (node && node->replaceTo)
  {
   if (buf.data == NULL)
   {
    /* initialize buffer */
    initStringInfo(&buf);
    /* insert any data we already skipped over */
    if (srcchar != srcstart)
     appendBinaryStringInfo(&buf, srcstart, srcchar - srcstart);
   }
   appendBinaryStringInfo(&buf, node->replaceTo, node->replacelen);
  }
  else
  {
   matchlen = pg_mblen_range(srcchar, srcend);
   if (buf.data != NULL)
    appendBinaryStringInfo(&buf, srcchar, matchlen);
  }

  srcchar += matchlen;
  len -= matchlen;
 }

 /* return a result only if we made at least one substitution */
 if (buf.data != NULL)
 {
  res = (TSLexeme *) palloc0(sizeof(TSLexeme) * 2);
  res->lexeme = buf.data;
  res->flags = TSL_FILTER;
 }
 else
  res = NULL;

 PG_RETURN_POINTER(res);
}

/*
 * Function-like wrapper for dictionary
 */

PG_FUNCTION_INFO_V1(unaccent_dict);
Datum
unaccent_dict(PG_FUNCTION_ARGS)
{
 text    *str;
 int   strArg;
 Oid   dictOid;
 TSDictionaryCacheEntry *dict;
 TSLexeme   *res;

 if (PG_NARGS() == 1)
 {
  /*
   * Use the "unaccent" dictionary that is in the same schema that this
   * function is in.
 */

  Oid   procnspid = get_func_namespace(fcinfo->flinfo->fn_oid);
  const char *dictname = "unaccent";

  dictOid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
          PointerGetDatum(dictname),
          ObjectIdGetDatum(procnspid));
  if (!OidIsValid(dictOid))
   ereport(ERROR,
     (errcode(ERRCODE_UNDEFINED_OBJECT),
      errmsg("text search dictionary \"%s.%s\" does not exist",
       get_namespace_name(procnspid), dictname)));
  strArg = 0;
 }
 else
 {
  dictOid = PG_GETARG_OID(0);
  strArg = 1;
 }
 str = PG_GETARG_TEXT_PP(strArg);

 dict = lookup_ts_dictionary_cache(dictOid);

 res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(dict->lexize),
              PointerGetDatum(dict->dictData),
              PointerGetDatum(VARDATA_ANY(str)),
              Int32GetDatum(VARSIZE_ANY_EXHDR(str)),
              PointerGetDatum(NULL)));

 PG_FREE_IF_COPY(str, strArg);

 if (res == NULL)
 {
  PG_RETURN_TEXT_P(PG_GETARG_TEXT_P_COPY(strArg));
 }
 else if (res->lexeme == NULL)
 {
  pfree(res);
  PG_RETURN_TEXT_P(PG_GETARG_TEXT_P_COPY(strArg));
 }
 else
 {
  text    *txt = cstring_to_text(res->lexeme);

  pfree(res->lexeme);
  pfree(res);

  PG_RETURN_TEXT_P(txt);
 }
}

Messung V0.5 in Prozent
C=95 H=90 G=92

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.22Bemerkung:  (vorverarbeitet am  2026-08-04) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002