/* |rot13|, a utility for performing ROT13 and other Caesar (or "shift") ciphers. Can be used as a filter, or on a list of text files. Written by Arthur O'Dwyer, 2002. Free for all uses. */ #include #include #include #include #include #include #define steq(x,y) (!strcmp((x),(y))) /* The upper-case English alphabet in order. */ const char Alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* Respective frequencies of the letters above in English text. */ const int CaesarFreq[] = { 810, 140, 270, 380, 1300, 290, 200, 520, 630, 13, 40, 340, 250, 710, 790, 190, 11, 680, 610, 1050, 240, 90, 150, 15, 190, 7 }; static char *Argv0; static int RotN = 13; static int GuessCaesar = 0; static int BufferAll = 0; static int BufSiz = BUFSIZ; void rotate(FILE *in, FILE *out); void rotate_all(FILE *in, FILE *out); void guess(FILE *in, FILE *out); void init_rotch(void); int rotch(int c); void do_error(const char *fmat, ...); void do_help(int man); int main(int argc, char *argv[]) { int i, j; Argv0 = argv[0]; for (i=1; i < argc; ++i) { char *tmp; int newRotN; if (*argv[i] && strchr("+-", *argv[i]) && isdigit(argv[i][1])) { newRotN = strtol(argv[i]+1, &tmp, 10); if (*tmp == '\0') { RotN = ((*argv[i] == '+')? newRotN: (26-newRotN%26)) % 26; continue; } } if (argv[i][0] != '-') break; if (argv[i][1] == '\0') break; if (steq(argv[i]+1, "-")) { ++i; break; } else if (steq(argv[i]+1, "?") || steq(argv[i]+1, "h") || steq(argv[i]+1, "-help")) do_help(0); else if (steq(argv[i]+1, "-man")) do_help(1); else if (steq(argv[i]+1, "B") || steq(argv[i]+1, "b")) { int newBufSiz; if (i >= argc-1) do_error("Need an argument with -B"); newBufSiz = strtol(argv[++i], &tmp, 10); if (*tmp != '\0' || newBufSiz <= 0) do_error("%s is not a valid positive integer!", argv[i]); BufSiz = newBufSiz; } else { for (j=1; argv[i][j]; ++j) { if (toupper(argv[i][j])=='C') GuessCaesar = 1; else if (toupper(argv[i][j])=='A') BufferAll = 1; else do_error("Unrecognized option(s) %s; use" " --help for more information", argv[i]); } } } init_rotch(); if (i == argc) { if (GuessCaesar) guess(stdin, stdout); else if (BufferAll) rotate_all(stdin, stdout); else rotate(stdin, stdout); } else { for (; i < argc; i++) { FILE *infp = steq(argv[i], "-")? stdin: fopen(argv[i], "r"); if (infp == NULL) do_error("File does not exist: %s", argv[i]); if (GuessCaesar) guess(infp, stdout); else if (BufferAll) rotate_all(infp, stdout); else rotate(infp, stdout); fclose(infp); } } return EXIT_SUCCESS; } void rotate(FILE *in, FILE *out) { int ch; while ((ch = getc(in)) != EOF) putc(rotch((unsigned char)ch), out); } void rotate_all(FILE *in, FILE *out) { char *buf = malloc(10); int len = 0, cap = 10; int i, ch; while ((ch = getc(in)) != EOF) { if (len == cap) { void *t = realloc(buf, 3*cap/2); if (t) { buf = t; cap = 3*cap/2; } else { for (i=0; i < len; ++i) putc(buf[i], out); len = 0; } } buf[len++] = rotch((unsigned char)ch); } for (i=0; i < len; ++i) putc(buf[i], out); free(buf); return; } void guess(FILE *in, FILE *out) { char *buf = malloc(BufSiz); int blen; int freq[26] = {0}; int freqtotal = 0; int perftotal; int i; int c; int best_ssq, best_rotn=0; if (buf == NULL) do_error("No memory... try rot13 -C -B %d instead", BufSiz/2); for (blen=0; (blen < BufSiz) && ((c = getc(in)) != EOF); ++blen) { char *p; buf[blen] = c; if (c && (p = strchr(Alpha, toupper(c))) != NULL) { ++freq[p - Alpha]; ++freqtotal; } } /* calculate total of correct letter frequencies */ for (perftotal=0, i=0; i < 26; ++i) perftotal += CaesarFreq[i]; /* try to make a good match to the data in freq[] */ best_ssq = INT_MAX; for (i=0; i < 26; ++i) { int j; int ssq = 0; for (j=0; j < 26; ++j) { int r = (100*CaesarFreq[j]/perftotal) - (100*freq[(j+26-i)%26]/freqtotal); ssq += r*r; } if (ssq < best_ssq) { best_ssq = ssq; best_rotn = i; } } RotN = best_rotn; init_rotch(); for (i=0; i < blen; ++i) putc(rotch(buf[i]), out); if (!feof(in)) rotate(in, out); } static int rtable[UCHAR_MAX]; void init_rotch(void) { int i; for (i=0; i < UCHAR_MAX; ++i) rtable[i] = i; for (i=0; Alpha[i]; ++i) { rtable[(int)Alpha[i]] = Alpha[(i+RotN) % 26]; rtable[tolower(Alpha[i])] = tolower(rtable[(int)Alpha[i]]); } } int rotch(int c) { return rtable[c]; } void do_error(const char *fmat, ...) { va_list ap; printf("%s: ", Argv0); va_start(ap, fmat); vprintf(fmat, ap); printf("\n"); va_end(ap); exit(EXIT_FAILURE); } void do_help(int man) { if (man) goto man; man: puts("rot13 [-?h] [-n] [-C [-B n]] [filenames]"); puts("Applies ROT13 or a variant to a text file."); puts(" -n (e.g., +4, -20): rotate by n letters"); puts(" -C: caesar(6); guess rotation based on letter count"); printf(" -B n: specify size of letter-count buffer (currently %d)\n", BufSiz); exit(0); }