/* Copyright (C) 2004
 * Andy Goth <unununium@openverse.com>
 *
 * This code is available under the GNU General Public License; see COPYING. */

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

#define I2H(i) ((i) < 10 ? (i) + '0' - 0x0 : (i) + 'a' - 0xa)

#define H2D(c) ((c) >= '0' && (c) <= '9' ? (c) + 0x0 - '0' : \
                (c) >= 'a' && (c) <= 'f' ? (c) + 0xa - 'a' : \
                (c) >= 'A' && (c) <= 'F' ? (c) + 0xa - 'A' : 0)

#define LITERALS ",./':1234567890-=_+|!#$^\n" \
                 "abcdefghijklmnopqrstuvwxyz" \
                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

int main(int argc, char** argv)
{
    int reverse;
    int i, ret;
    int state = 0, accum;
    unsigned char c, buf[4096], prev[3];

    /* The world's dumbest option scanner. :^) */
    switch (argc) {
    case 1:  reverse = 0; break;
    case 2:  reverse = 1; break;
    default: fprintf(stderr, "Usage: urlesc [-r]\n"); return EXIT_SUCCESS;
    }

    while (1) {
        ret = read(0, buf, 4096);

        if (ret == -1) {
            perror("read");
            return EXIT_FAILURE;
        } else if (ret == 0) {
            return EXIT_SUCCESS;
        } else {
            /* Fall through. */
        }

        for (i = 0; i < ret; ++i) {
            c = buf[i];
            if (reverse) {
                if (state == 0) {
                    if (c == '%') {
                        prev[0] = '%';
                        state = 1;
                        accum = 0;
                    } else {
                        write(1, &c, 1);
                    }
                } else {
                    prev[state] = c;
                    if (isxdigit(c)) {
                        ++state;
                        accum = accum * 16 + H2D(c);
                        if (state == 3) {
                            c = accum;
                            write(1, &c, 1);
                            state = 0;
                        }
                    } else if (c == '%') {
                        write(1, &prev, state);
                        prev[0] = '%';
                        state = 1;
                        accum = 0;
                    } else {
                        write(1, &prev, state + 1);
                        state = 0;
                    }
                }
            } else {
                if (strchr(LITERALS, c) == NULL) {
                    prev[0] = '%';
                    prev[1] = I2H(c / 16);
                    prev[2] = I2H(c % 16);
                    write(1, &prev, 3);
                } else {
                    write(1, &c, 1);
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

/* vim: set ts=4 sts=4 sw=4 tw=80 et: */

