#include #include #include #include #include #include #include #include const char* cmdname; void die(const char* filename) { stralloc msg = {0}; if (stralloc_copys(&msg, cmdname) && stralloc_cats(&msg, ": ") && stralloc_cats(&msg, strerror(errno)) && stralloc_cats(&msg, ": ") && stralloc_cats(&msg, filename) && stralloc_cats(&msg, "\n")) { write(2, msg.s, msg.len); } exit(1); } void invalid_char_sequence(const char* chars) { stralloc msg = {0}; if (stralloc_copys(&msg, cmdname) && stralloc_cats(&msg, ": invalid character sequence: ") && stralloc_cats(&msg, chars) && stralloc_cats(&msg, "\n")) { write(2, msg.s, msg.len); } exit(1); } void bye(const char* errormsg) { stralloc msg = {0}; if (stralloc_copys(&msg, cmdname) && stralloc_cats(&msg, errormsg) && stralloc_cats(&msg, "\n")) { write(2, msg.s, msg.len); } exit(1); } void nomem() { static const char msg[] = "Out of memory.\n"; write(2, msg, sizeof msg - 1); exit(1); } void scan_char_sequence(const char* chars, stralloc* seq) { enum {START, GOT_CHAR, IN_RANGE} state = START; char last; for (const unsigned char* cp = (const unsigned char*) chars; *cp; ++cp) { if (state == START || (state == GOT_CHAR && *cp != '-')) { if (!stralloc_readyplus(seq, 1)) nomem(); last = seq->s[seq->len++] = *cp; state = GOT_CHAR; } else if (state == GOT_CHAR) { state = IN_RANGE; } else { if (*cp < last) invalid_char_sequence(chars); if (!stralloc_readyplus(seq, *cp - last)) nomem(); for (unsigned char ch = last + 1; ch <= *cp; ++ch) { seq->s[seq->len++] = ch; } state = START; } } if (state == IN_RANGE) invalid_char_sequence(chars); } int main(int argc, const char* argv[]) { cmdname = *argv++; --argc; if (argc != 3) { stralloc usage = {0}; if (stralloc_copys(&usage, "Usage: ") && stralloc_cats(&usage, cmdname) && stralloc_cats(&usage, " chars1 chars2 file\n")) { write(2, usage.s, usage.len); } exit(1); } const char* chars1 = *argv++; --argc; const char* chars2 = *argv++; --argc; const char* file = *argv++; --argc; stralloc seq1 = {0}; scan_char_sequence(chars1, &seq1); stralloc seq2 = {0}; scan_char_sequence(chars2, &seq2); if (seq1.len != seq2.len) bye("chars1 and chars2 have different lengths"); unsigned char table[256]; for (unsigned int i = 0; i < sizeof table; ++i) { table[i] = i; } for (unsigned int i = 0; i < seq1.len; ++i) { table[(unsigned int) seq1.s[i]] = seq2.s[i]; } int fd = open(file, O_RDWR); if (fd < 0) die(file); struct stat statbuf; if (fstat(fd, &statbuf) < 0) die(file); off_t nbytes = statbuf.st_size; unsigned char* buf = (unsigned char*) mmap(0, nbytes, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) die(file); for (unsigned char* cp = buf; cp < buf + nbytes; ++cp) { unsigned char ch = *cp; if (table[ch] != ch) { *cp = table[ch]; } } if (munmap(buf, nbytes) < 0) die(file); if (close(fd) < 0) die(file); }