<|start_filename|>Makefile<|end_filename|>

OS = $(shell uname)
CC ?= gcc
AR = ar
LD = ld
LN = ln
RM = rm
VALGRIND ?= valgrind
STRIP = strip
PREFIX ?= /usr/local
DESTDIR ?= utf8
VERSION_MAJOR = 0
VERSION_MINOR = 1
LIB_NAME = utf8
TARGET_NAME = lib$(LIB_NAME)
TARGET_STATIC = $(TARGET_NAME).a
TARGET_DSOLIB = $(TARGET_NAME).so.$(VERSION_MAJOR).$(VERSION_MINOR)
TARGET_DYLIB = $(TARGET_NAME).$(VERSION_MAJOR).$(VERSION_MINOR).dylib
TARGET_DSO = $(TARGET_NAME).so
CFLAGS ?= -I. -std=c99 -Wall -fvisibility=hidden -O2 -fPIC -pedantic
LDFLAGS ?= -shared -soname $(TARGET_DSO).$(VERSION_MAJOR)
OSX_LDFLAGS ?= -lc -Wl,-install_name,$(TARGET_DSO), -o $(TARGET_DSOLIB)
SRC = $(wildcard *.c)
OBJS = $(SRC:.c=.o)
TEST_SRC = $(filter-out test/test.c, $(wildcard test/*.c))
TEST_OBJS = $(TEST_SRC:.c=.o)
TEST_MAIN = utf8-test

ifneq ("Darwin","$(OS)")
	CFLAGS += -lm
endif

ifdef DEBUG
	CFLAGS += -DUTF8_DEBUG
endif

all: $(TARGET_STATIC) $(TARGET_DSO)

$(TARGET_STATIC): clean $(OBJS)
	$(AR) crus $(TARGET_STATIC) $(OBJS)

$(TARGET_DSO): clean $(OBJS)
ifeq ("Darwin","$(OS)")
	$(CC) -shared $(OBJS) $(OSX_LDFLAGS) -o $(TARGET_DSOLIB)
	$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO)
	$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO).$(VERSION_MAJOR)
else
	$(LD) $(OBJS) $(LDFLAGS) -o $(TARGET_DSOLIB)
	$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO)
	$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO).$(VERSION_MAJOR)
	$(STRIP) --strip-unneeded $(TARGET_DSO)
endif

src/.c.o:
	$(CC) $(CFLAGS) -c $<

test/.c.o:
	$(CC) $(CFLAGS) -c $<

check: test
	$(VALGRIND) --leak-check=full ./$(TEST_MAIN)

test: $(TEST_OBJS)
	$(CC) $(TEST_OBJS) test/test.c ./$(TARGET_STATIC) $(CFLAGS) -o $(TEST_MAIN)
	./$(TEST_MAIN)

clean:
	$(RM) -f $(TEST_MAIN)
	$(RM) -f $(OBJS)
	$(RM) -f $(TEST_OBJS)
	$(RM) -f $(TARGET_STATIC)
	$(RM) -f $(TARGET_DSOLIB)
	$(RM) -f $(TARGET_DSO).$(VERSION_MAJOR)
	$(RM) -f $(TARGET_DSO)
	$(RM) -f $(TARGET_DYLIB)

install:
	test -d $(PREFIX)/$(DESTDIR) || mkdir $(PREFIX)/$(DESTDIR)
	install $(LIB_NAME).h $(PREFIX)/include/$(DESTDIR)
	install $(TARGET_STATIC) $(PREFIX)/lib
	install $(TARGET_DSO) $(PREFIX)/lib

uninstall:
	rm -rf $(PREFIX)/$(DESTDIR)
	rm -f $(PREFIX)/lib/$(TARGET_STATIC)
	rm -f $(PREFIX)/lib/$(TARGET_DSO)

.PHONY: test


<|start_filename|>test/encode.c<|end_filename|>

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utf8.h>
#include "common.h"

#define d utf8_decode
#define e utf8_encode

static char *
t (const char *dec, const char *enc) {
  char *r = e(dec);
  assert(r);
  assert(0 == strcmp(enc, r));
  return r;
}

TEST(encode) {
  char *enc = NULL;

  // 1 byte
  enc = e("\x5C");
  assert('\x5C' == enc[0]);
  free(enc);

  enc = e("\x7F");
  assert('\x7F' == enc[0]);
  free(enc);

  enc = e("\x0");
  assert('\0' == enc[0]);
  free(enc);

  enc = e("\x1");
  assert('\x1' == enc[0]);
  free(enc);

  enc = e("\x2");
  assert('\x2' == enc[0]);
  free(enc);

  enc = e("\x2");
  assert('\x2' == enc[0]);
  free(enc);

  enc = e("\x3");
  assert('\x3' == enc[0]);
  free(enc);

  enc = e("\x4");
  assert('\x4' == enc[0]);
  free(enc);

  enc = e("\x5");
  assert('\x5' == enc[0]);
  free(enc);

  enc = e("\x6");
  assert('\x6' == enc[0]);
  free(enc);

  enc = e("\x7");
  assert('\x7' == enc[0]);
  free(enc);

  enc = e("\x8");
  assert('\x8' == enc[0]);
  free(enc);

  enc = e("\x9");
  assert('\x9' == enc[0]);
  free(enc);

  enc = e("\x0a");
  assert('\x0a' == enc[0]);
  free(enc);

  enc = e("\x0b");
  assert('\x0b' == enc[0]);
  free(enc);

  enc = e("\x0c");
  assert('\x0c' == enc[0]);
  free(enc);

  enc = e("\x0d");
  assert('\x0d' == enc[0]);
  free(enc);

  enc = e("\x0e");
  assert('\x0e' == enc[0]);
  free(enc);

  enc = e("\x0f");
  assert('\x0f' == enc[0]);
  free(enc);

  enc = e("\x21");
  assert('\x21' == enc[0]);
  free(enc);

  enc = e("\x22");
  assert('\x22' == enc[0]);
  free(enc);

  enc = e("\x23");
  assert('\x23' == enc[0]);
  free(enc);

  enc = e("\x24");
  assert('\x24' == enc[0]);
  free(enc);

  enc = e("\x25");
  assert('\x25' == enc[0]);
  free(enc);

  enc = e("\x26");
  assert('\x26' == enc[0]);
  free(enc);

  printf("encode: 1 byte ok\n");

  // 2 byte
  enc = e("\x80");
  assert('\xC2' == enc[0]);
  assert('\x80' == enc[1]);
  free(enc);

  enc = e("\xa1");
  assert('\xc2' == enc[0]);
  assert('\xa1' == enc[1]);
  free(enc);

  enc = e("\xa2");
  assert('\xc2' == enc[0]);
  assert('\xa2' == enc[1]);
  free(enc);

  enc = e("\xa3");
  assert('\xc2' == enc[0]);
  assert('\xa3' == enc[1]);
  free(enc);

  enc = e("\xa4");
  assert('\xc2' == enc[0]);
  assert('\xa4' == enc[1]);
  free(enc);

  enc = e("\xa5");
  assert('\xc2' == enc[0]);
  assert('\xa5' == enc[1]);
  free(enc);

  enc = e("\xa6");
  assert('\xc2' == enc[0]);
  assert('\xa6' == enc[1]);
  free(enc);

  enc = e("\xa7");
  assert('\xc2' == enc[0]);
  assert('\xa7' == enc[1]);
  free(enc);

  enc = e("\xa8");
  assert('\xc2' == enc[0]);
  assert('\xa8' == enc[1]);
  free(enc);

  enc = e("\xa9");
  assert('\xc2' == enc[0]);
  assert('\xa9' == enc[1]);
  free(enc);

  printf("encode: 2 byte ok\n");

  // 3 byte
  free(t("𐌈", "ð"));
  free(t("Ⴀ", "á "));
  free(t("Ⴥ", "á"));
  free(t("ლ", "á"));
  free(t("ᚙ", "á"));
  free(t("ᚏ", "á"));

  printf("encode: 3 byte ok\n");

  // unmatched surrogate halves
  free(t("���", "ï¿½ï¿½ï¿½"));
  free(t("������", "ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½"));
  free(t("���A", "ï¿½ï¿½ï¿½A"));
  free(t("���𝌆���", "ï¿½ï¿½ï¿½ðï¿½ï¿½ï¿½"));
  free(t("���", "ï¿½ï¿½ï¿½"));

  printf("encode: unmatched surrogate halve ok\n");

  // interpolation
  // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt
  free(t("∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i)",
    "â® Eâda = Q,  n â â, â f(i) = â g(i)"));

  free(t("STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑",
    "STARGÎÌTE SG-1, a = vÌ = rÌ, aâ â¥ bâ"));

  printf("encode: interpolation ok\n");

  return 0;
}


<|start_filename|>utf8.h<|end_filename|>

/**
 * libutf8  - `utf8.h'
 *
 * (c) 2013 <NAME> <<EMAIL>>
 */

#ifndef UTF8_H
#define UTF8_H 1

#include <errno.h>

#if __GNUC__ >= 4
# define UTF8_EXTERN __attribute__((visibility("default")))
#else
# define UTF8_EXTERN
#endif

/**
 * Defines the maxium buffer length
 * for a encode/decode buffer
 */

#define UTF8_MAX_BUFFER_LENGTH 4096

/**
 * Tests whether `a' is in range of `b' and `c'
 */

#define UTF8_IN_URANGE(a,b,c) (a >= (unsigned int) b && a <= (unsigned int) c)

/**
 * Prints a utf8 encode/decode
 * error to `stderr' defaults to
 * `perror(str)'
 */

#define utf8_perror(str) {                                                     \
  switch (errno) {                                                             \
    case 0: break;                                                             \
    case UTF8E_EOF_BYTES_NEEDED:                                               \
      fprintf(stderr, "%s: (EOF_BYTES_NEEDED)\n", str);                        \
      break;                                                                   \
    case UTF8E_CODE_POINT_OOB:                                                 \
      fprintf(stderr, "%s: (CODE_POINT_OOB)\n", str);                          \
      break;                                                                   \
    case UTF8E_BYTE_RANGE:                                                     \
      fprintf(stderr, "%s: (BYTE_RANGE)\n", str);                              \
      break;                                                                   \
    case UTF8E_BYTE_BOUNDARY_RANGE:                                            \
      fprintf(stderr, "%s: (BYTE_BOUNDARY_RANGE)\n", str);                     \
      break;                                                                   \
    default: perror(str);                                                      \
  }                                                                            \
}

/**
 * Defines a set of error types
 */

typedef enum utf8_errors {
    UTF8E_EOF_BYTES_NEEDED = 1
  , UTF8E_CODE_POINT_OOB
  , UTF8E_BYTE_RANGE
  , UTF8E_BYTE_BOUNDARY_RANGE
} utf8_error_t;

/**
 * Returns a utf8 encoded string from
 * a given utf8 decoded string.
 *
 * Return value:
 *  Returns a `char *' or `NULL'
 *  in the case of an error.
 *
 * Error:
 *  In the occurence of an error
 *  `errno' will be populated with
 *  an error code which can be
 *  interpreted with `utf8_perror(str)'
 */

UTF8_EXTERN char *
utf8_encode (const char *);

/**
 * Returns a utf8 decoded string
 * from a given utf8 encoded string.
 *
 * Return value:
 *  Returns a `char *' or `NULL'
 *  in the case of an error.
 *
 * Error:
 *  In the occurence of an error
 *  `errno' will be populated with
 *  an error code which can be
 *  interpreted with `utf8_perror(str)'
 */

UTF8_EXTERN char *
utf8_decode (const char *);

#endif


<|start_filename|>clib.json<|end_filename|>
{
  "name": "utf8",
  "version": "0.1.1",
  "description": "A whatwg compliant UTF8 encoding and decoding library",
  "repo": "jwerle/libutf8",
  "src": [
    "Makefile",
    "utf8.h",
    "encode.c",
    "decode.c"
  ],
  "keywords": [
    "utf8",
    "charset",
    "encoding",
    "decoding"
  ],
  "author": "<NAME>",
  "license": "MIT"
}

<|start_filename|>test/common.h<|end_filename|>

#ifndef UTF8_TEST_H
#define UTF8_TEST_H 1

#include <assert.h>

#define UTF8_STRINGIFY_HELPER(x) #x
#define UTF8_STRINGIFY(x) UTF8_STRINGIFY_HELPER(x)

#define D(name) int utf8_test_##name ();

#define T(name) { \
  printf("%s: checking..\n", UTF8_STRINGIFY(name)); \
  assert(0 == utf8_test_##name ()); \
  printf("%s: pass\n", UTF8_STRINGIFY(name)); \
}

#define TEST(name) int utf8_test_##name ()

D(encode)
D(decode)

#endif


<|start_filename|>package.json<|end_filename|>
{
  "name": "jwerle/@libutf8",
  "version": "0.1.1",
  "description": "A whatwg compliant UTF8 encoding and decoding C99 library",
  "scripts": {
    "version": "./scripts/update-clib-json && git add clib.json",
    "test": "make test"
  },
  "repository": {
    "type": "git",
    "url": "<EMAIL>:jwerle/libutf8.git"
  },
  "keywords": [
    "utf8",
    "charset",
    "encoding",
    "decoding"
  ],
  "author": "<NAME>",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/jwerle/libutf8/issues"
  }
}


<|start_filename|>test/decode.c<|end_filename|>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <utf8.h>
#include "common.h"

#define d utf8_decode
#define e utf8_encode

static char *
t (const char *enc, const char *dec) {
  char *r = d(enc);
  assert(r);
  assert(0 == strcmp(dec, r));
  return r;
}

TEST(decode) {
  char *dec = NULL;

  // 1 byte
  dec = d("\x5C");
  assert('\x5C' == dec[0]);
  free(dec);

  dec = d("\x7F");
  assert('\x7F' == dec[0]);
  free(dec);

  dec = d("\x1");
  assert('\x1' == dec[0]);
  free(dec);

  dec = d("\x2");
  assert('\x2' == dec[0]);
  free(dec);

  dec = d("\x2");
  assert('\x2' == dec[0]);
  free(dec);

  dec = d("\x3");
  assert('\x3' == dec[0]);
  free(dec);

  dec = d("\x4");
  assert('\x4' == dec[0]);
  free(dec);

  dec = d("\x5");
  assert('\x5' == dec[0]);
  free(dec);

  dec = d("\x6");
  assert('\x6' == dec[0]);
  free(dec);

  dec = d("\x7");
  assert('\x7' == dec[0]);
  free(dec);

  dec = d("\x8");
  assert('\x8' == dec[0]);
  free(dec);

  dec = d("\x9");
  assert('\x9' == dec[0]);
  free(dec);

  dec = d("\x0a");
  assert('\x0a' == dec[0]);
  free(dec);

  dec = d("\x0b");
  assert('\x0b' == dec[0]);
  free(dec);

  dec = d("\x0c");
  assert('\x0c' == dec[0]);
  free(dec);

  dec = d("\x0d");
  assert('\x0d' == dec[0]);
  free(dec);

  dec = d("\x0e");
  assert('\x0e' == dec[0]);
  free(dec);

  dec = d("\x0f");
  assert('\x0f' == dec[0]);
  free(dec);

  dec = d("\x21");
  assert('\x21' == dec[0]);
  free(dec);

  dec = d("\x22");
  assert('\x22' == dec[0]);
  free(dec);

  dec = d("\x23");
  assert('\x23' == dec[0]);
  free(dec);

  dec = d("\x24");
  assert('\x24' == dec[0]);
  free(dec);

  dec = d("\x25");
  assert('\x25' == dec[0]);
  free(dec);

  dec = d("\x26");
  assert('\x26' == dec[0]);
  free(dec);

  printf("decode: 1 byte ok\n");

  // 2 byte
  dec = t("\u00c2\u00a1", "\u00a1");
  assert('\xc2' == dec[0]);
  assert('\xa1' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a2", "\u00a2");
  assert('\xc2' == dec[0]);
  assert('\xa2' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a3", "\u00a3");
  assert('\xc2' == dec[0]);
  assert('\xa3' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a4", "\u00a4");
  assert('\xc2' == dec[0]);
  assert('\xa4' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a5", "\u00a5");
  assert('\xc2' == dec[0]);
  assert('\xa5' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a6", "\u00a6");
  assert('\xc2' == dec[0]);
  assert('\xa6' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a7", "\u00a7");
  assert('\xc2' == dec[0]);
  assert('\xa7' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a8", "\u00a8");
  assert('\xc2' == dec[0]);
  assert('\xa8' == dec[1]);
  free(dec);

  dec = t("\u00c2\u00a9", "\u00a9");
  assert('\xc2' == dec[0]);
  assert('\xa9' == dec[1]);
  free(dec);

  printf("decode: 2 byte ok\n");

  // 3 byte
  free(t("ð", "𐌈"));
  free(t("á ", "Ⴀ"));
  free(t("á", "Ⴥ"));
  free(t("á", "ლ"));
  free(t("á", "ᚙ"));
  free(t("á", "ᚏ"));

  printf("decode: 3 byte ok\n");

  // unmatched surrogate halves
  free(t("ï¿½ï¿½ï¿½", "���"));
  free(t("ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½", "������"));
  free(t("ï¿½ï¿½ï¿½A", "���A"));
  free(t("ï¿½ï¿½ï¿½ðï¿½ï¿½ï¿½", "���𝌆���"));
  free(t("ï¿½ï¿½ï¿½", "���"));

  printf("decode: unmatched surrogate halve ok\n");

  // interpolation
  free(t("â® Eâda = Q,  n â â, â f(i) = â g(i)",
        "∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i)"));

  free(t("STARGÎÌTE SG-1, a = vÌ = rÌ, aâ â¥ bâ",
        "STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑"));

  printf("encode: interpolation ok\n");

  if (errno > 0) {
    utf8_perror("decode error:");
  }

  return 0;
}


<|start_filename|>test/test.c<|end_filename|>

#include <stdio.h>
#include <string.h>
#include <utf8.h>
#include "common.h"

int
main (void) {
  printf("\nlibutf8: running tests\n\n");

  T(encode);
  T(decode);

  char str[] = "Foo © bar 𝌆 baz ☃ qux";
  char *enc = NULL; 
  char *dec = NULL;

  enc = utf8_encode(str);

  if (NULL == enc) {
    utf8_perror("encode");
    return 1;
  }

  dec = utf8_decode(enc);

  if (NULL == dec) {
    utf8_perror("decode");
    return 1;
  }

  assert(0 == strcmp(str, dec));

  printf("\nlibutf8: all tests ok!\n");
  return 0;
}
