#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <alloca.h>
#include <errno.h>
#include <string.h>
#define t(...) do { if (__VA_ARGS__) goto fail; } while (0)
static const char* argv0;
char* compare(const char* restrict e, const char* restrict c)
{
size_t en = strlen(e);
size_t cn = strlen(c);
size_t** matrix = alloca((en + 1) * sizeof(size_t*));
char** path_matrix = alloca((en + 1) * sizeof(char*));
size_t ei, ci, ri = 0;
char* rc = NULL;
for (ei = 0; ei <= en; ei++)
{
matrix[ei] = alloca((cn + 1) * sizeof(size_t));
matrix[ei][0] = ei;
path_matrix[ei] = alloca((cn + 1) * sizeof(char));
path_matrix[ei][0] = '|';
}
for (ci = 0; ci <= cn; ci++)
{
matrix[0][ci] = ci;
path_matrix[0][ci] = '-';
}
path_matrix[0][0] = '\0';
e--, c--;
for (ei = 1; ei <= en; ei++)
for (ci = 1; ci <= cn; ci++)
{
size_t d = matrix[ei - 1][ci - 1];
size_t l = matrix[ei + 0][ci - 1];
size_t u = matrix[ei - 1][ci + 0];
size_t lu = 1 + (l < u ? l : u);
int ch = (e[ei] != c[ci]);
d += (size_t)ch;
matrix[ei][ci] = d < lu ? d : lu;
path_matrix[ei][ci] = d < lu ? (ch ? '\\' : '=') : (l < u ? '-' : '|');
}
t (!(rc = malloc((en + cn + 2) * sizeof(char))));
#if 0
for (ei = 0; ei <= en; ei++)
{
for (ci = 0; ci <= cn; ci++)
printf("%zu%s", matrix[ei][ci], ci == cn ? "" :
path_matrix[ei][ci + 1] == '-' ? "\033[1;32m-\033[m" : " ");
printf("\n");
if (ei < en)
for (ci = 0; ci < cn; ci++)
printf("%s%s", path_matrix[ei + 1][ci + 0] == '|' ? "\033[1;31m|\033[m" : " ",
path_matrix[ei + 1][ci + 1] == '\\' ? "\033[1;33m\\\033[m" :
path_matrix[ei + 1][ci + 1] == '=' ? "\033[1;39m\\\033[m" : " ");
printf("\n");
}
#endif
rc[ri++] = '\0';
for (ei = en, ci = cn; ei + ci;)
switch (path_matrix[ei][ci])
{
case '=': ei--, ci--; rc[ri++] = '='; break;
case '\\': ei--, ci--; rc[ri++] = '!'; break;
case '|': ei--; rc[ri++] = '-'; break;
case '-': ci--; rc[ri++] = '+'; break;
default:
abort();
}
rc[ri] = '\0';
fail:
return rc;
}
int main(int argc, char* argv[])
{
char* correct;
char* entered = NULL;
size_t n = 0;
void* new;
char* w;
char* r;
char* path = NULL;
char* path_;
char* p;
int rc = 0;
argv0 = argc ? argv[0] : "learn-your-telephone-number";
if (argc != 2)
goto usage;
correct = alloca((2 * strlen(argv[1]) + 1) * sizeof(char));
for (w = correct, r = argv[1]; *r; r++)
if (isdigit(*r)) *w++ = *r;
else if (isalpha(*r)) goto usage;
else if (*r == '+') *w++ = '0', *w++ = '0';
*w = '\0';
t (fprintf(stdout, "Enter your telephone number: ") < 0);
t (fflush(stdout));
t (errno = 0, getline(&entered, &n, stdin) < 0);
n = strlen(entered) - 1;
entered[n] = '\0';
t (!(new = realloc(entered, (2 * n + 1) * sizeof(char))));
entered = new;
memmove(r = (entered + n), w = (entered), n + 1);
for (; *r; r++)
if (isdigit(*r)) *w++ = *r;
else if (*r == '+') *w++ = '0', *w++ = '0';
*w = '\0';
if (!strcmp(entered, correct))
goto done;
t (!(path = compare(entered, correct)));
path_ = strchr(path + 1, '\0');
t (fprintf(stdout, "\033[A\033[KEntered: ") < 0);
for (p = entered; *--path_;)
switch (*path_)
{
case '=': t (fprintf(stdout, "\033[39m%c", *p++) < 0); break;
case '!': t (fprintf(stdout, "\033[31m%c", *p++) < 0); break;
case '+': t (fprintf(stdout, " ") < 0); break;
case '-': t (fprintf(stdout, "\033[31m%c", *p++) < 0); break;
}
t (fprintf(stdout, "\033[m\n") < 0);
path_ = strchr(path + 1, '\0');
t (fprintf(stdout, "Correct: ") < 0);
for (p = correct; *--path_;)
switch (*path_)
{
case '=': t (fprintf(stdout, "\033[39m%c", *p++) < 0); break;
case '!': t (fprintf(stdout, "\033[32m%c", *p++) < 0); break;
case '+': t (fprintf(stdout, "\033[32m%c", *p++) < 0); break;
case '-': t (fprintf(stdout, " ") < 0); break;
}
t (fprintf(stdout, "\033[m\n") < 0);
rc = 1;
done:
free(entered);
free(path);
return rc;
fail:
rc = 1;
if (errno == 0)
goto done;
perror(argv0);
rc = 2;
goto done;
usage:
fprintf(stderr, "usage: %s YOUR-TELEPHONE-NUMBER\n", argv0);
return 2;
}