/* leetspeak.c - Don Yang (uguu.org) May need to add functionality so that certain substitutions only match uppercase/lowercase source, not both. lclint 2.5m: 37 errors -boolops -compdef -compmempass -immediatetrans -mustfree -predboolint -retvalint -type -usedef 04/08/01 */ #include #include #include #include #include #define DICTIONARY_SIZE 15 #define MAX_WORD_LENGTH 8 #define THRESHOLD 4 /* Custom character type */ typedef struct { int c; int ucase; int punct; } Character; /* Conversion dictionary (sort for greedy matching) */ static char *Dictionary[DICTIONARY_SIZE][2] = { {"computer", "box"}, {"dude", "d00d"}, {"elite", "l33t"}, {"you.", "j00."}, {"the.", "da."}, {"il", "i1"}, {"li", "1i"}, {"s.", "z."}, {"a", "4"}, {"e", "3"}, {"i", "1"}, {"l", "1"}, {"o", "0"}, {"s", "5"}, {"t", "7"} }; /* Globals */ static Character CurrentChar; static Character Stack[MAX_WORD_LENGTH + 1]; static Character Buffer[MAX_WORD_LENGTH + 1]; static int BufSize, StackSize, EndOfFile; /* Prototypes */ static int Compare(Character *str1, char *str2, int length); static void Convert(Character *dst, int src); static int ExactMatch(void); static int Match(int *index); static Character *NextChar(FILE *infile); static void Output(char *str, Character *ref, int length); static void OutputChar(Character *c); static void PushChar(Character *c); /******************************************************************** main */ int main(int argc, char **argv) { FILE *infile; Character *c; int index; srand((unsigned)time(NULL)); if( argc > 1 ) { if( (infile = fopen(argv[1], "rt")) == NULL ) return printf("Can not open %s\n", argv[1]); } else { infile = stdin; } /* Process input until file/buffer/stack is completely read */ StackSize = BufSize = EndOfFile = 0; for(c = NextChar(infile); BufSize || StackSize || !EndOfFile; c = NextChar(infile)) { Buffer[BufSize].c = c->c; Buffer[BufSize].ucase = c->ucase; Buffer[BufSize++].punct = c->punct; switch( Match(&index) ) { case 1: /* Exactly one match in dictionary */ if( strlen(Dictionary[index][0]) == (size_t)BufSize ) { /* Print when complete entry matched */ Output(Dictionary[index][1], Buffer, BufSize); BufSize = 0; } break; case 0: /* Nothing matched, try push back extra characters */ while( BufSize > 1 ) { PushChar(&Buffer[--BufSize]); if( (index = ExactMatch()) > -1 ) { Output(Dictionary[index][1], Buffer, BufSize); BufSize = 0; break; } } /* Do no push back first character to prevent infinite loop */ if( BufSize == 1 ) { if( (index = ExactMatch()) > -1 ) Output(Dictionary[index][1], Buffer, BufSize); else OutputChar(&Buffer[0]); BufSize = 0; } break; default: /* More than one match, need to read more characters */ break; } } /* End */ if( infile != stdin ) fclose(infile); return 0; } /* main() */ /******************************************************************* Compare Compare strings. */ static int Compare(Character *str1, char *str2, int length) { Character c; int i; for(i = 0; i < length; i++) { Convert(&c, str2[i]); if( !str2[i] || str1[i].c != c.c ) return 1; } return 0; } /* Compare() */ /******************************************************************* Convert Convert ASCII character to internal character. */ static void Convert(Character *dst, int src) { if( !isalpha(src) ) { dst->c = '.'; dst->ucase = 0; dst->punct = src; if( src == EOF ) EndOfFile = 1; } else { dst->c = tolower(src); dst->ucase = isupper(src); dst->punct = 0; } } /* Convert() */ /**************************************************************** ExactMatch find exact match in dictionary, return dictionary index. */ static int ExactMatch(void) { int i; for(i = 0; i < DICTIONARY_SIZE; i++) { if( strlen(Dictionary[i][0]) == (size_t)BufSize && !Compare(Buffer, Dictionary[i][0], BufSize) ) return i; } return -1; } /* ExactMatch() */ /********************************************************************* Match Find partial entry in dictionary, return number of matches. */ static int Match(int *index) { int i, count; for(i = count = 0; i < DICTIONARY_SIZE; i++) { if( !Compare(Buffer, Dictionary[i][0], BufSize) ) { count++; *index = i; } } return count; } /* Match() */ /****************************************************************** NextChar Get next character. */ static Character *NextChar(FILE *infile) { if( StackSize ) return &Stack[--StackSize]; Convert(&CurrentChar, EndOfFile ? EOF : fgetc(infile)); return &CurrentChar; } /* NextChar() */ /******************************************************************** Output Output string, preserving case and punctuation. */ static void Output(char *str, Character *ref, int length) { int i, u; /* Output some strings as plain text */ if( (rand() % THRESHOLD) == 0 ) { for(i = 0; i < length; i++) { if( ref[i].punct ) putchar(ref[i].punct); else putchar(ref[i].ucase ? toupper(ref[i].c) : ref[i].c); } return; } /* Write first character */ if( ref[0].punct ) putchar(ref[0].punct); else putchar(ref[0].ucase ? toupper(str[0]) : str[0]); /* Write remaining characters */ if( strlen(str) > 1 ) { u = ref[1].ucase; for(i = 1; i < strlen(str) - 1; i++) putchar(u ? toupper(str[i]) : str[i]); if( ref[length - 1].punct ) putchar(ref[length - 1].punct); else putchar(u ? toupper(str[strlen(str) - 1]) : str[strlen(str) - 1]); } } /* Output() */ /**************************************************************** OutputChar Write single untranslated character. */ static void OutputChar(Character *c) { if( c->punct == EOF ) return; if( c->punct ) putchar(c->punct); else putchar(c->ucase ? toupper(c->c) : c->c); } /* OutputChar() */ /****************************************************************** PushChar Push character onto stack. */ static void PushChar(Character *c) { Stack[StackSize].c = c->c; Stack[StackSize].ucase = c->ucase; Stack[StackSize++].punct = c->punct; } /* PushChar() */