← Back to davo.co
summaryrefslogtreecommitdiffstats
path: root/analyze-utf8.c
blob: 8e8f881951264d4014b78393a0ac9518392c85e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "mbstrings.h"
#include <uchar.h>
#include <stdio.h>
#include <locale.h>

int main(void) {
  // Make sure to have the platform's mb encoding on input.
  setlocale(LC_CTYPE, "");
  // Holds the state of input/output buffering.
  mbstate_t st = { };
  // collects the input mb sequence
  char ib[23];
  // collects the current UTF-8 mb sequence
  char8_t ob[5] = { };
  // the number of input characters for the current code point
  size_t in = 0;
  while (fgets(ib, sizeof(ib), stdin)) {
    // Run through the current line. The last character is
    // always reserved for the string terminator.
    for (char* p = ib; (p-ib) < sizeof(ib)-1;) {
      size_t const n = sizeof(ib)-1-(p-ib);
      size_t const r = mbrtoc8(ob, p, n, &st);
      switch (r) {       // Handle the special cases.
      case mbincomplete: p += n; in += n;    continue;
      case 0: case mbstored: case mbinvalid: goto INVAL;
      }
      p += r; in += r;
      char8_t* cont = ob+1;  // first character is already stored
      while (mbrtoc8(cont, "", 1, &st) == mbstored) {
        cont++;
      }
      // Now we have the whole UTF-8. Analyze the result.
      printf("%s", ((cont-ob) == 1) ? "ASCII\t" : "UTF-8\t");
      for (char8_t* o = ob; o < cont; ++o) {
        printf("|%02hhx", *o);
      }
      // fgets stopped at an end of a line
      if (*ob == u8'\n') {
        puts("|\t~ eol");
        in = 0;
        break;
      } else if (in == (cont-ob)) {
        printf("|\t~ '%s'\n", ob);
      } else {
        printf("|\t%zu→%tu\n", in, (cont-ob));
      }
      in = 0;
    }
    if (*ob != u8'\n') {
      fputs("incomplete line\n", stderr);
    }
  }
  return EXIT_SUCCESS;
 INVAL:
  fputs("input error, exiting\n", stderr);
  return EXIT_FAILURE;
}