#include #define TAB_SIZE 8 /* Convert hexadecimal digit. */ static int Convert(char d) { return d >= '0' && d <= '9' ? d - '0' : (d >= 'a' && d <= 'f') || (d >= 'A' && d <= 'F') ? (d & 15) + 9 : -1; } /* Rewrite escape sequences in string. */ static void Unescape(char *r) { char *w = r; for(; *r; r++) { *w++ = *r != '\\' ? *r : *++r != 'n' ? *r != 'r' ? *r != 't' ? *r != 'b' ? *r != 'a' ? *r != 'v' ? *r != 'x' ? *r : Convert(r[1]) >= 0 ? Convert(r[2]) >= 0 ? (r += 2), Convert(r[-1]) * 16 + Convert(*r) : Convert(*++r) : 'x' : '\v' : '\a' : '\b' : '\t' : '\r' : '\n'; } *w = '\0'; } int main(int argc, char **argv) { int n, s, length, bit, target; char *p, *q; if( argc != 3 ) return printf("%s {msg1} {msg2}\n", *argv); /* Output header. */ puts("#include/*\n" "$o=\"*/\n" "static int l = 1, I, i, o;" "int main(int O,char**s){O=O?O:"); p = argv[1]; q = argv[2]; Unescape(p); Unescape(q); for(; *p || *q; p++, q++) { int c_tab = 1; /* Column number with tabs expanded. */ int c_space = 1; /* Column number with only character counts. */ /* Encode a single byte. First message is encoded using tab-expanded column numbers, second message is encoded using unexpanded column numbers. If bytes for both messages were identical, we only need to insert spaces until we get the desired column number. If they are different, we first brute-force search for the right mix of space and tabs to get the delta between the two bytes, then insert spaces to get the column numbers we wanted. We aim for the right delta first before getting the desired column numbers as opposed to brute-forcing both column numbers in one go, since doing the latter would require trying significantly more combinations. (We can also avoid brute-force by doing a bit of dynamic programming to pre-generate a table of tab+space mix, but the expectation is that the messages will be short anyways). To reduce brute-force search space size and line length, each byte is encoded in two nibbles. */ for(n = 4; n >= 0; n -= 4) { const int delta = ((*p >> n) - (*q >> n)) & 0x0f; /* Encoded sequence of space+tabs to be appended to achieve the desired delta. First character is the least significant bit and last character is the most significant "1" bit. Each "0" bit encodes a space and each "1" bit encodes a tab. We don't need a separate variable to store the sequence length since the highest bit is always "1". This is because all non-empty sequences will always end with a trailing tab, since trailing spaces do not affect deltas. */ int append = 0; if( delta != 0 ) { for(length = 0; !append; length++) { for(s = 0; s < (1 << length); s++) { int ct = c_tab; int cs; for(bit = 0; bit < length; bit++) { if( (s & (1 << bit)) != 0 ) ct += TAB_SIZE - ((ct - 1) % TAB_SIZE); else ct++; } cs = c_space + length; if( ((ct - cs) & 0x0f) == delta ) append = s; } } } /* Output adjustment sequence. */ for(; append != 0; append /= 2) { if( (append & 1) != 0 ) { putchar('\t'); c_tab += TAB_SIZE - ((c_tab - 1) % TAB_SIZE); } else { putchar(' '); c_tab++; } c_space++; } /* Increment column numbers until we get the desired bits. The -1 is to account for the expression prefix. */ target = ((*p >> n) - 1) & 0x0f; while( (c_tab & 0x0f) != target ) { putchar(' '); c_tab++; c_space++; } /* Output expression. */ printf("O/0-"); c_tab += 4; c_space += 4; } putchar('\n'); /* Once the shorter string has reached the end, pad all the remaining bytes with the longer string, so that all characters in the suffix do not have any deltas. */ if( *p == '\0' ) p = q; else if( *q == '\0' ) q = p; } /* Output footer. */ #define Q(p, q) puts(#p "," #q); Q( **s; while( l && (O = getchar()) - EOF ) { l = l - 100 ? l - 105 ? O - 58 ? O > 47 && O < 58 ? (i = i * 10 + O - 48), l : 10 - O ? 100 - O || l - 5 ? l : O : 1 : 2 - l ? l + 1 : (i = 0) + 3 : O - 118 ? 6 : o ? (I |= i & 15) ? (o = !putchar(I)) + 8 : 0 : 11 + (I = (i & 15) << (o = 4)) : O - 'i' ? 6 : O; } return 0; ) puts( "}/*\";" "$t = $x = $y = $z = 1;" "$r = \"#{" "$t = 8;" "$o.each_byte{|i|" "$i = i;" "%q[\";" "if( $r )" "{" "foreach $i (unpack 'C*', $o)" "{" "#];\n" "$i == 47 && $z == 79 && print($0, ':', $y + 1, ':', $x, \":: div\\n\");" "$x += $i == 9 ? $t - ~-$x % $t : 1;" "$y += $i == 10 ? $x = 1 : 0;" "$z = $i;" "}" "}" "#\"#*/" ); return 0; }