PHPのstrtr関数っぽいものをC言語で実装してみた

# 発端

<blockquote class="twitter-tweet" lang="ja"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/%E8%B5%B7%E3%81%8D%E3%81%9F%E3%82%89%E3%81%BE%E3%81%A3%E3%81%B4%E3%83%BC%E5%B8%AB%E3%81%8Cstrtr%E3%81%AEC%E5%AE%9F%E8%A3%85%E3%82%92%E5%AE%8C%E6%88%90%E3%81%95%E3%81%9B%E3%81%A6%E3%82%8B?src=hash">#起きたらまっぴー師がstrtrのC実装を完成させてる</a></p>&mdash; 又横顔 (@tadsan) <a href="https://twitter.com/tadsan/status/636957867350716421">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# 経緯

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">編集リクエスト (改竄)&#10;<a href="http://t.co/A7u4QRgMiV">http://t.co/A7u4QRgMiV</a></p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636953494893694977">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">原形がない…</p>&mdash; 又横顔 (@tadsan) <a href="https://twitter.com/tadsan/status/636954333158899712">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">str_replaceだけ触れてstrtrを説明していない記事って腐るほどあるけどそれを見るたびstrtrが気の毒になる</p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636955479726403584">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">“一度置換した文字列はもう置換の対象にならないことを確認しましょう”のあたりが実装めんどくさそう (まっぴー氏の実装まだちゃんと読んでない)</p>&mdash; 又横顔 (@tadsan) <a href="https://twitter.com/tadsan/status/636956271665504256">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/tadsan">@tadsan</a> PHPの関数使ってるのでまだラクですけどこれCでいちから実装するのかなりエグいですｗｗ</p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636956711635390464">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">Cでstrtr実装してみたさあるぞ</p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636957453171601408">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/%E8%B5%B7%E3%81%8D%E3%81%9F%E3%82%89%E3%81%BE%E3%81%A3%E3%81%B4%E3%83%BC%E5%B8%AB%E3%81%8Cstrtr%E3%81%AEC%E5%AE%9F%E8%A3%85%E3%82%92%E5%AE%8C%E6%88%90%E3%81%95%E3%81%9B%E3%81%A6%E3%82%8B?src=hash">#起きたらまっぴー師がstrtrのC実装を完成させてる</a></p>&mdash; 又横顔 (@tadsan) <a href="https://twitter.com/tadsan/status/636957867350716421">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">全ての元凶&#10;<a href="https://t.co/Y43lyhmxj8">https://t.co/Y43lyhmxj8</a></p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636975473042452482">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">寝れない💢</p>&mdash; 進捗 (@mpyw) <a href="https://twitter.com/mpyw/status/636975522984030208">2015, 8月 27</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# コード

<del>`strtr_strings`の方に不具合があります。C言語詳しい人おしえてください。</del>

- <del>`malloc`してるはずなのに`free`出来ないって言われる</del> 修正完了
- <del>文字列の末尾付近の置換結果がおかしい</del> 修正完了

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct memo_t {
    size_t pos;
    size_t len;
} Memo;

int compare_memo(const void *a, const void *b) {
    return (ssize_t)((const Memo*)b)->len - (ssize_t)((const Memo*)a)->len;
}

char *strtr_chars(char *str, const char *from, const char *to) {
    char *s = str;
    while (*s) {
        const char *f = from;
        const char *t = to;
        while (*f && *t) {
            if (*s == *f) {
                *s = *t;
                break;
            }
            ++f;
            ++t;
        }
        ++s;
    }
    return str;
}

char *strtr_strings(const char *str, size_t size, const char *from[size], const char *to[size], char **buffer) {
    Memo memo[size];
    for (size_t i = 0; i < size; ++i) {
        memo[i].pos = i;
        memo[i].len = strlen(from[i]);
    }
    qsort(memo, size, sizeof(Memo), compare_memo);
    size_t capacity = strlen(str) + 1;
    size_t filled = 0;
    if (!(*buffer = (char *)malloc(capacity))) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    char *ptr = *buffer;
    while (*str) {
        for (size_t i = 0; i < size; ++i) {
            size_t pos = memo[i].pos;
            const char *haystack = str;
            const char *needle = from[pos];
            while (*haystack && *needle && *haystack == *needle) {
                ++haystack;
                ++needle;
            }
            if (!*needle) {
                needle = from[pos];
                const char *replace = to[pos];
                while (*needle++) {
                    ++str;
                }
                while (*replace) {
                    if (capacity - filled < 1) {
                        size_t pos = ptr - *buffer;
                        *buffer = realloc(*buffer, capacity += 1024);
                        if (!*buffer) {
                            perror("realloc");
                            exit(EXIT_FAILURE);
                        }
                        ptr = *buffer + pos;
                    }
                    ++filled; 
                    *ptr++ = *replace++;
                }
                break;
            } else if (i == size - 1) {
                if (capacity - filled < 1) {
                    size_t pos = ptr - *buffer;
                    *buffer = realloc(*buffer, capacity += 1024);
                    if (!*buffer) {
                        perror("realloc");
                        exit(EXIT_FAILURE);
                    }
                    ptr = *buffer + pos;
                }
                ++filled; 
                *ptr++ = *str++;
            }
        }
    }
    *ptr = '\0';
    return *buffer;
}

int main(void) {
    char str[] = "abaabbabcababcabbabc";
    char *ptr;
    const char *from_strings[] = {"a", "ab", "abc"};
    const char *to_strings[] = {"<a>", "<ab>", "<abc>"};
    printf("%s\n", strtr_strings(
        str, sizeof(from_strings) / sizeof(char *), from_strings, to_strings, &ptr
    ));
    printf("%s\n", strtr_chars(str, "abc", "cab"));
    free(ptr);
    return 0;
}
```