Logo Search packages:      
Sourcecode: latrine version File versions  Download package

charset.c

/* vim: set noet ts=4:
 *
 * Copyright (c) 2002-2007 Martin A. Godisch <martin@godisch.de>.
 *
 * 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 of the License, 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., 51 Franklin
 * St, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#include <charset.h>
#include <iconv.h>
#include <langinfo.h>
#include <latrine.h>
#include <memory.h>
#include <stdio.h>

#define MAX_KEYMAP 256

static char*
      local_charset = NULL;
static int
      mapcount[2] = {0, 0};
static struct {
      wchar_t from;
      char *to;
} mapping[2][MAX_KEYMAP];

/* make digraph character
 */
inline void make_digraph(wint_t alpha, wint_t *special)
{
      switch (alpha) {
      case 'A': switch (*special) {
                  case '\'': *special = 193; break;
                  case ':' : *special = 196; break;
      } break;
      case 'a': switch (*special) {
                  case '\'': *special = 225; break;
                  case ':' : *special = 228; break;
      } break;
      case 'C': switch (*special) {
                  case '<' : *special = 268; break;
      } break;
      case 'c': switch (*special) {
                  case '<' : *special = 269; break;
      } break;
      case 'D': switch (*special) {
                  case '<' : *special = 270; break;
      } break;
      case 'd': switch (*special) {
                  case '<' : *special = 271; break;
      } break;
      case 'E': switch (*special) {
                  case '\'': *special = 201; break;
      } break;
      case 'e': switch (*special) {
                  case '\'': *special = 233; break;
      } break;
      case 'I': switch (*special) {
                  case '\'': *special = 205; break;
      } break;
      case 'i': switch (*special) {
                  case '\'': *special = 237; break;
      } break;
      case 'L': switch (*special) {
                  case '\'': *special = 313; break;
                  case '<' : *special = 317; break;
      } break;
      case 'l': switch (*special) {
                  case '\'': *special = 314; break;
                  case '<' : *special = 318; break;
      } break;
      case 'N': switch (*special) {
                  case '?' : *special = 209; break;
                  case '<' : *special = 327; break;
      } break;
      case 'n': switch (*special) {
                  case '?' : *special = 241; break;
                  case '<' : *special = 328; break;
      } break;
      case 'O': switch (*special) {
                  case '\'': *special = 211; break;
                  case '>' : *special = 212; break;
      } break;
      case 'o': switch (*special) {
                  case '\'': *special = 243; break;
                  case '>' : *special = 244; break;
      } break;
      case 'R': switch (*special) {
                  case '\'': *special = 340; break;
      } break;
      case 'r': switch (*special) {
                  case '\'': *special = 341; break;
      } break;
      case 'S': switch (*special) {
                  case '<' : *special = 352; break;
      } break;
      case 's': switch (*special) {
                  case '<' : *special = 353; break;
      } break;
      case 'T': switch (*special) {
                  case '<' : *special = 356; break;
      } break;
      case 't': switch (*special) {
                  case '<' : *special = 357; break;
      } break;
      case 'U': switch (*special) {
                  case '\'': *special = 218; break;
      } break;
      case 'u': switch (*special) {
                  case '\'': *special = 250; break;
      } break;
      case 'Y': switch (*special) {
                  case '\'': *special = 221; break;
      } break;
      case 'y': switch (*special) {
                  case '\'': *special = 253; break;
      } break;
      case 'Z': switch (*special) {
                  case '<' : *special = 381; break;
      } break;
      case 'z': switch (*special) {
                  case '<' : *special = 382; break;
      } break;
      case '!': switch (*special) {
                  case 'I' : *special = 161; break;
      } break;
      case '?': switch (*special) {
                  case 'I' : *special = 191; break;
      } break;
      default:
            if (debug)
                  fprintf(debug, "make_digraph: unknown digraph: %d\n", alpha);
      }
}

/* check, whether a given character in a string is a quoted one
 *
 * returns  1: quoted, i.e. odd count of backslashes before
 * returns  0: not quoted
 * returns -1: invalid arguments
 */
static inline int quoted(const char *start, const char *pos)
{
      char *s;

      if (start == NULL || pos < start)
            return -1;
      for (s = (char*)pos - 1; *s == '\\' && s >= start; s--);
      return (pos - s + 1) & 1;
}

/* load key mapping
 *
 * returns  0: success
 * returns -1: failure
 */
int load_keymap(int i, const char *file)
{
      char buffer[BUFSIZE];
      wchar_t buf2[BUFSIZE];
      FILE *F  = NULL;
      char *s  = NULL;
      char *t  = NULL;
      int line = 0;

      assert(i == 0 || i == 1);
      if (file == NULL)
            return 0;
      if ((F = fopen(file, "r")) == NULL) {
            errmsg("fopen: %s: %m", file);
            return -1;
      }
      mapcount[i] = 0;
      for (line = 1; fgets(buffer, BUFSIZE, F) != 0; line++) {
            if (*buffer == '\n' || *buffer == '#')
                  continue;
            if ((s = index(buffer, '\n')) != NULL) /* FIXME: handle NL */
                  *s = 0;
            for (t = buffer; (t = index(t, '\t')) != NULL && quoted(buffer, t); t++);
            if (t == NULL) {
                  errmsg(_("%s:%d: ignoring invalid key mapping"), file, line);
                  continue;
            }
            *t++ = 0;
            for (s = buffer; (s = index(s, '\\')) != NULL; s++)
                  memmove(s, s + 1, strlen(s) + 1);
            for (s = t; (s = index(s, '\\')) != NULL; s++)
                  memmove(s, s + 1, strlen(s) + 1);
            mbstowcs(buf2, buffer, BUFSIZE);
            mapping[i][mapcount[i]].from = *buf2;
            mapping[i][mapcount[i]].to   = STRDUP(t);
            if (debug)
                  fprintf(debug, "adding key mapping for input %d: %X -> %s\n", i, mapping[i][mapcount[i]].from, mapping[i][mapcount[i]].to);
            if (++mapcount[i] >= MAX_KEYMAP) {
                  errmsg(_("internal error: %s, please contact the author"), "load_keymap: mapcount >= MAX_KEYMAP");
                  fclose(F);
                  return -1;
            }
      }
      fclose(F);
      return 0;
}

/* apply key mapping
 *
 * returns  1: success, mapping
 * returns  0: success, no mapping
 * returns -1: failure, no mapping
 */
static int map_key(int i, wchar_t c, char *buffer, size_t buflen)
{
      size_t j, n;

      for (j = 0; j < mapcount[i]; j++)
            if (c == mapping[i][j].from) {
                  if ((n = strlen(mapping[i][j].to)) >= buflen)
                        return -1;
                  memcpy(buffer, mapping[i][j].to, n + 1);
                  return 1;
            }
      return 0;
}

/* convert a string
 *
 * returns the number of resulting bytes
 * returns -1: failure
 */
static size_t convert(iconv_t *cd, const char *to, const char *from, char *inbuf, size_t inlen, size_t outlen)
{
      char *outbuf = NULL;
      char *p      = inbuf;
      char *q      = NULL;
      size_t n     = outlen;

      if (*cd == NULL && (*cd = iconv_open(to, from)) == (iconv_t)(-1)) {
            errmsg("iconv_open: %m");
            return -1;
      }
      q = outbuf = (char*)MALLOC(outlen);
      iconv(*cd, NULL, &inlen, &outbuf, &outlen);
      if (iconv(*cd, &inbuf, &inlen, &outbuf, &outlen) == -1) {
            if (debug)
                  fprintf(debug, "convert: iconv (%s, %s): %m\n", to, from);
            outlen = -1;
      /** FIXME
      else if (outlen == 0) {
            errno  = E2BIG;
            outlen = -1;
      */
      } else {
            memset(p, 0, n);
            memcpy(p, q, outlen = n - outlen);
      }
      FREE(&q);
      return outlen;
}

/* convert a wchar_t string from local charset to UTF-8
 *
 * returns  0: success
 * returns -1: failure
 * exits if local charset is unknown
 */
size_t utf2local(char *buffer, size_t buflen)
{
      static iconv_t cd = NULL;

      if (local_charset == NULL && (local_charset = nl_langinfo(CODESET)) == NULL) {
            errmsg("nl_langinfo: %m");
            exit(-1);
      }
      return convert(&cd, local_charset, "UTF-8", buffer, strlen(buffer), buflen);
}

/* convert a wchar_t string from ISO-8859-1 to UTF-8
 *
 * returns  0: success
 * returns -1: failure
 */
size_t iso2utf(char *buffer, size_t buflen)
{
      static iconv_t cd = NULL;
      return convert(&cd, "UTF-8", "ISO-8859-1", buffer, strlen(buffer), buflen);
}

/* return the length of a wchar_t string
 *
 * returns  0: success
 * returns -1: failure
 */
inline size_t wcstrlen(const wchar_t* buffer)
{
      size_t i;

      if (buffer == NULL)
            return -1;
      for (i = 0; buffer[i] != 0L; i++);
      return i;
}

/* append a wchar_t to a wchar_t string
 *
 * returns  0: success
 * returns -1: buffer overflow
 */
inline int wcsappend(int i, wchar_t *buffer, wchar_t c, size_t buflen)
{
      char    buf1[BUFSIZE];
      wchar_t buf2[BUFSIZE];
      size_t  n;

      if (buffer == NULL)
            return -1;
      switch (map_key(i, c, buf1, BUFSIZE)) {
      case 1:
            mbstowcs(buf2, buf1, BUFSIZE);
            if (wcstrlen(buffer) + wcstrlen(buf2) >= buflen) {
                  errmsg(_("internal error: %s, please contact the author"), "wcsappend: buffer + buf2 >= buflen");
                  return -1;
            }
            memcpy(buffer + wcstrlen(buffer), buf2, (wcstrlen(buf2) + 1) * sizeof(wchar_t));
            return 0;
      case 0:
            if ((n = wcstrlen(buffer)) + 1 >= buflen) {
                  errmsg(_("internal error: %s, please contact the author"), "wcsappend: buffer + 1 >= buflen");
                  return -1;
            }
            buffer[n++] = c;
            buffer[n] = 0L;
            return 0;
      case -1:
            errmsg(_("internal error: %s, please contact the author"), "wcsappend: case -1");
            return -1;
      }
      assert(0);
}

/* remove the last character from a wchar_t string
 *
 * returns  0: success
 * returns -1: empty string
 */
inline int wcsbackspace(wchar_t *buffer, wchar_t *c)
{
      wchar_t *p;

      if (c != NULL)
            *c = 0;
      if (buffer == NULL || *buffer == 0L)
            return -1;
      for (p = buffer; *p != 0L; p++);
      --p;
      if (c != NULL)
            *c = *p;
      *p = 0L;
      return 1;
}

/* reverse a wchar_t string
 *
 * returns: nothing
 */
inline void wcsreverse(wchar_t *buffer)
{
      wchar_t *p;
      size_t i, n;

      if (buffer == NULL)
            return;
      p = MALLOC((n = wcstrlen(buffer)) * sizeof(wchar_t));
      for (i = 0; i < n; i++)
            p[i] = buffer[n-i-1];
      memcpy(buffer, p, n * sizeof(wchar_t));
      free(p);
}

/* add an per-wchar_t offset to a wchar_t string
 *
 * returns  0: success
 * returns -1: failure
 */
inline void wcsaddn(wchar_t* buffer, unsigned offset)
{
      wchar_t *c;

      for (c = buffer; *c != 0L; c++)
            *c += offset;
}

Generated by  Doxygen 1.6.0   Back to index