diff options
| author | Kevin Lange <k@dakko.us> | 2011-12-03 19:49:30 -0600 | 
|---|---|---|
| committer | Kevin Lange <k@dakko.us> | 2011-12-03 19:49:30 -0600 | 
| commit | cedbee69a2f2a647c01ef3fcec946decd4f24959 (patch) | |
| tree | 0b8a821e06e6800e771f2f4beef4eb6abc7add43 /src | |
| parent | 93fcebb156a9c7ceea8ea2677d5ac3084f6387d2 (diff) | |
| download | nyancat-cedbee69a2f2a647c01ef3fcec946decd4f24959.tar.gz | |
Comments? In /my/ code? It's more likely than you think.
Diffstat (limited to 'src')
| -rw-r--r-- | src/nyancat.c | 201 | 
1 files changed, 143 insertions, 58 deletions
| diff --git a/src/nyancat.c b/src/nyancat.c index 7554630..5fa8825 100644 --- a/src/nyancat.c +++ b/src/nyancat.c @@ -4,6 +4,16 @@   * Developed by: Kevin Lange   *               http://github.com/klange/nyancat   * + * This is a simple telnet server / standalone application which renders the + * classic Nyan Cat (or "poptart cat") to your terminal. + * + * It makes use of various ANSI escape sequences to render color, or in the case + * of a VT220, simply dumps text to the screen. + * + * For more information, please see: + * + *     http://miku.acm.uiuc.edu + *   * Permission is hereby granted, free of charge, to any person obtaining a copy   * of this software and associated documentation files (the "Software"), to   * deal with the Software without restriction, including without limitation the @@ -37,54 +47,95 @@  #include <signal.h>  #include "telnet.h" +/* The animation frames are stored separately. */  #include "animation.h" + +/* The color palette is not yet set */  char * colors[256] = {NULL}; +/* + * For most modes, we ouput spaces, but for some + * we will use block characters. + */  char * output = "  "; +/* + * These values crop the animation, as we have a full 64x64 stored, + * but we only want to display 80x24. + */  #define MIN_ROW 20  #define MAX_ROW 44 -  #define MIN_COL 10  #define MAX_COL 50 +/* + * In the standalone mode, we want to handle an interrupt signal + * (^C) so that we can restore the cursor. + */  void SIGINT_handler(int sig){  	printf("\033[?25h");  	exit(0);  } +/* + * These are the options we want to use as + * a telnet server. These are set in set_options() + */  unsigned char telnet_options[256] = { 0 };  unsigned char telnet_willack[256] = { 0 }; +/* + * These are the values we have set or + * agreed to during our handshake. + * These are set in send_command(...) + */  unsigned char telnet_do_set[256]  = { 0 };  unsigned char telnet_will_set[256]= { 0 }; +/* + * Set the default options for the telnet server. + */  void set_options() { +	/* We will not echo input */  	telnet_options[ECHO] = WONT; +	/* We will set graphics modes */  	telnet_options[SGA]  = WILL; +	/* We will not set new environments */  	telnet_options[NEW_ENVIRON] = WONT; +	/* The client should echo its own input */  	telnet_willack[ECHO]  = DO; +	/* The client can set a graphics mode */  	telnet_willack[SGA]   = DO; +	/* The client should not change the window size */  	telnet_willack[NAWS]  = DONT; +	/* The client should tell us its terminal type (very important) */  	telnet_willack[TTYPE] = DO; +	/* No linemode */  	telnet_willack[LINEMODE] = DONT; +	/* And the client can set a new environment */  	telnet_willack[NEW_ENVIRON] = DO;  }  void send_command(int cmd, int opt) { +	/* Send a command to the telnet client */  	if (cmd == DO || cmd == DONT) { +		/* DO commands say what the client should do. */  		if (((cmd == DO) && (telnet_do_set[opt] != DO)) ||  			((cmd == DONT) && (telnet_do_set[opt] != DONT))) { +			/* And we only send them if there is a disagreement */  			telnet_do_set[opt] = cmd;  			printf("%c%c%c", IAC, cmd, opt);  		}  	} else if (cmd == WILL || cmd == WONT) { +		/* Similarly, WILL commands say what the server will do. */  		if (((cmd == WILL) && (telnet_will_set[opt] != WILL)) ||  			((cmd == WONT) && (telnet_will_set[opt] != WONT))) { +			/* And we only send them during disagreements */  			telnet_will_set[opt] = cmd;  			printf("%c%c%c", IAC, cmd, opt);  		}  	} else { +		/* Other commands are sent raw */  		printf("%c%c", IAC, cmd);  	}  } @@ -92,17 +143,23 @@ void send_command(int cmd, int opt) {  int main(int argc, char ** argv) { +	/* I have a bad habit of being very C99, so this may not be everything */ +	/* The default terminal is ANSI */ +	char term[1024] = {'a','n','s','i'};  	int k, ttype;  	uint32_t option = 0, done = 0, sb_mode = 0, do_echo = 0; -	char term[1024] = {'a','n','s','i'}; +	/* Various pieces for the telnet communication */  	char sb[1024] = {0};  	char sb_len   = 0;  	if (argc > 1) {  		if (!strcmp(argv[1], "-t")) { +			/* Yes, I know, lame way to get arguments, whatever, we only want one of them. */ +			/* Set the default options */  			set_options(); +			/* Let the client know what we're using */  			for (option = 0; option < 256; option++) {  				if (telnet_options[option]) {  					send_command(telnet_options[option], option); @@ -116,13 +173,15 @@ int main(int argc, char ** argv) {  				}  			} +			/* Negotiate options */  			while (!feof(stdin) && !done) {  				unsigned char i = getchar();  				unsigned char opt = 0; -				if (i == 255) { +				if (i == IAC) {  					i = getchar();  					switch (i) {  						case SE: +							/* End of extended option mode */  							sb_mode = 0;  							if (sb[0] == TTYPE) {  								strcpy(term, &sb[2]); @@ -130,11 +189,13 @@ int main(int argc, char ** argv) {  							}  							break;  						case NOP: +							/* No Op */  							send_command(NOP, 0);  							fflush(stdout);  							break;  						case WILL:  						case WONT: +							/* Will / Won't Negotiation */  							opt = getchar();  							if (!telnet_willack[opt]) {  								telnet_willack[opt] = WONT; @@ -142,70 +203,82 @@ int main(int argc, char ** argv) {  							send_command(telnet_willack[opt], opt);  							fflush(stdout);  							if ((i == WILL) && (opt == TTYPE)) { +								/* WILL TTYPE? Great, let's do that now! */  								printf("%c%c%c%c%c%c", IAC, SB, TTYPE, SEND, IAC, SE);  								fflush(stdout);  							}  							break;  						case DO:  						case DONT: +							/* Do / Don't Negotation */  							opt = getchar();  							if (!telnet_options[opt]) {  								telnet_options[opt] = DONT;  							}  							send_command(telnet_options[opt], opt);  							if (opt == ECHO) { +								/* We don't really need this, as we don't accept input, but, +								 * in case we do in the future, set our echo mode */  								do_echo = (i == DO);  							}  							fflush(stdout);  							break;  						case SB: +							/* Begin Extended Option Mode */  							sb_mode = 1;  							sb_len  = 0;  							memset(sb, 0, 1024);  							break;  						case IAC:  -							/* Connection Closed */ +							/* Connection Closed During Negotiation */  							done = 1;  							break;  						default:  							break;  					}  				} else if (sb_mode) { -					sb[sb_len] = i; -					sb_len++; +					/* Extended Option Mode -> Accept character */ +					if (sb_len < 1023) { +						sb[sb_len] = i; +						sb_len++; +					}  				}  			}  		}  	} else { +		/* Otherwise, we were run standalone, find the terminal type from the environement */  		char * nterm = getenv("TERM");  		strcpy(term, nterm);  	}  ready: +	/* Convert the entire terminal string to lower case */  	for (k = 0; k < strlen(term); ++k) {  		term[k] = tolower(term[k]);  	} +	/* Do our terminal detection */  	if (strstr(term, "xterm")) { -		ttype = 1; +		ttype = 1; /* 256-color, spaces */  	} else if (strstr(term, "linux")) { -		ttype = 3; +		ttype = 3; /* Spaces and blink attribute */  	} else if (strstr(term, "vtnt")) { -		ttype = 5; +		ttype = 5; /* Extended ASCII fallback == Windows */  	} else if (strstr(term, "cygwin")) { -		ttype = 5; +		ttype = 5; /* Extended ASCII fallback == Windows */  	} else if (strstr(term, "vt220")) { -		ttype = 6; +		ttype = 6; /* No color support */  	} else if (strstr(term, "fallback")) { -		ttype = 4; +		ttype = 4; /* Unicode fallback */  	} else if (strstr(term, "rxvt")) { -		ttype = 3; +		ttype = 3; /* Accepts LINUX mode */  	} else { -		ttype = 2; +		ttype = 2; /* Verything else */  	} -	int always_escape = 0; +	int always_escape = 0; /* Used for text mode */ +	/* Accept ^C -> restore cursor */  	signal(SIGINT, SIGINT_handler);  	switch (ttype) {  		case 1: @@ -257,63 +330,65 @@ ready:  			colors['%']  = "\033[5;45m";     /* Pink cheeks */  			break;  		case 4: -			colors[',']  = "\033[0;34;44m";       /* Blue background */ -			colors['.']  = "\033[1;37;47m";       /* White stars */ -			colors['\''] = "\033[0;30;40m";       /* Black border */ -			colors['@']  = "\033[1;37;47m";       /* Tan poptart */ -			colors['$']  = "\033[1;35;45m";       /* Pink poptart */ -			colors['-']  = "\033[1;31;41m";       /* Red poptart */ -			colors['>']  = "\033[1;31;41m";       /* Red rainbow */ -			colors['&']  = "\033[0;33;43m";       /* Orange rainbow */ -			colors['+']  = "\033[1;33;43m";       /* Yellow Rainbow */ -			colors['#']  = "\033[1;32;42m";       /* Green rainbow */ -			colors['=']  = "\033[1;34;44m";       /* Light blue rainbow */ -			colors[';']  = "\033[0;34;44m";       /* Dark blue rainbow */ -			colors['*']  = "\033[1;30;40m";       /* Gray cat face */ -			colors['%']  = "\033[1;35;45m";       /* Pink cheeks */ +			colors[',']  = "\033[0;34;44m";  /* Blue background */ +			colors['.']  = "\033[1;37;47m";  /* White stars */ +			colors['\''] = "\033[0;30;40m";  /* Black border */ +			colors['@']  = "\033[1;37;47m";  /* Tan poptart */ +			colors['$']  = "\033[1;35;45m";  /* Pink poptart */ +			colors['-']  = "\033[1;31;41m";  /* Red poptart */ +			colors['>']  = "\033[1;31;41m";  /* Red rainbow */ +			colors['&']  = "\033[0;33;43m";  /* Orange rainbow */ +			colors['+']  = "\033[1;33;43m";  /* Yellow Rainbow */ +			colors['#']  = "\033[1;32;42m";  /* Green rainbow */ +			colors['=']  = "\033[1;34;44m";  /* Light blue rainbow */ +			colors[';']  = "\033[0;34;44m";  /* Dark blue rainbow */ +			colors['*']  = "\033[1;30;40m";  /* Gray cat face */ +			colors['%']  = "\033[1;35;45m";  /* Pink cheeks */  			output = "██";  			break;  		case 5: -			colors[',']  = "\033[0;34;44m";       /* Blue background */ -			colors['.']  = "\033[1;37;47m";       /* White stars */ -			colors['\''] = "\033[0;30;40m";       /* Black border */ -			colors['@']  = "\033[1;37;47m";       /* Tan poptart */ -			colors['$']  = "\033[1;35;45m";       /* Pink poptart */ -			colors['-']  = "\033[1;31;41m";       /* Red poptart */ -			colors['>']  = "\033[1;31;41m";       /* Red rainbow */ -			colors['&']  = "\033[0;33;43m";       /* Orange rainbow */ -			colors['+']  = "\033[1;33;43m";       /* Yellow Rainbow */ -			colors['#']  = "\033[1;32;42m";       /* Green rainbow */ -			colors['=']  = "\033[1;34;44m";       /* Light blue rainbow */ -			colors[';']  = "\033[0;34;44m";       /* Dark blue rainbow */ -			colors['*']  = "\033[1;30;40m";       /* Gray cat face */ -			colors['%']  = "\033[1;35;45m";       /* Pink cheeks */ +			colors[',']  = "\033[0;34;44m";  /* Blue background */ +			colors['.']  = "\033[1;37;47m";  /* White stars */ +			colors['\''] = "\033[0;30;40m";  /* Black border */ +			colors['@']  = "\033[1;37;47m";  /* Tan poptart */ +			colors['$']  = "\033[1;35;45m";  /* Pink poptart */ +			colors['-']  = "\033[1;31;41m";  /* Red poptart */ +			colors['>']  = "\033[1;31;41m";  /* Red rainbow */ +			colors['&']  = "\033[0;33;43m";  /* Orange rainbow */ +			colors['+']  = "\033[1;33;43m";  /* Yellow Rainbow */ +			colors['#']  = "\033[1;32;42m";  /* Green rainbow */ +			colors['=']  = "\033[1;34;44m";  /* Light blue rainbow */ +			colors[';']  = "\033[0;34;44m";  /* Dark blue rainbow */ +			colors['*']  = "\033[1;30;40m";  /* Gray cat face */ +			colors['%']  = "\033[1;35;45m";  /* Pink cheeks */  			output = "\333\333";  			break;  		case 6: -			colors[',']  = "::";       /* Blue background */ -			colors['.']  = "@@";       /* White stars */ -			colors['\''] = "  ";       /* Black border */ -			colors['@']  = "##";       /* Tan poptart */ -			colors['$']  = "??";       /* Pink poptart */ -			colors['-']  = "<>";       /* Red poptart */ -			colors['>']  = "##";       /* Red rainbow */ -			colors['&']  = "==";       /* Orange rainbow */ -			colors['+']  = "--";       /* Yellow Rainbow */ -			colors['#']  = "++";       /* Green rainbow */ -			colors['=']  = "~~";       /* Light blue rainbow */ -			colors[';']  = "$$";       /* Dark blue rainbow */ -			colors['*']  = ";;";       /* Gray cat face */ -			colors['%']  = "()";       /* Pink cheeks */ +			colors[',']  = "::";             /* Blue background */ +			colors['.']  = "@@";             /* White stars */ +			colors['\''] = "  ";             /* Black border */ +			colors['@']  = "##";             /* Tan poptart */ +			colors['$']  = "??";             /* Pink poptart */ +			colors['-']  = "<>";             /* Red poptart */ +			colors['>']  = "##";             /* Red rainbow */ +			colors['&']  = "==";             /* Orange rainbow */ +			colors['+']  = "--";             /* Yellow Rainbow */ +			colors['#']  = "++";             /* Green rainbow */ +			colors['=']  = "~~";             /* Light blue rainbow */ +			colors[';']  = "$$";             /* Dark blue rainbow */ +			colors['*']  = ";;";             /* Gray cat face */ +			colors['%']  = "()";             /* Pink cheeks */  			always_escape = 1;  			break;  		default:  			break;  	} +	/* Clear the screen */  	printf("\033[H\033[2J\033[?25l"); +	/* Display the MOTD */  	int countdown_clock = 5;  	for (k = 0; k < countdown_clock; ++k) {  		printf("\n\n\n"); @@ -333,9 +408,10 @@ ready:  		fflush(stdout);  		usleep(400000); -		printf("\033[H"); +		printf("\033[H"); /* Reset cursor */  	} +	/* Clear the screen again */  	printf("\033[H\033[2J\033[?25l");  	int playing = 1; @@ -343,27 +419,36 @@ ready:  	char last = 0;  	size_t y, x;  	while (playing) { +		/* Render the frame */  		for (y = MIN_ROW; y < MAX_ROW; ++y) {  			for (x = MIN_COL; x < MAX_COL; ++x) {  				if (always_escape) { +					/* Text mode (or "Always Send Color Escapse") */  					printf("%s", colors[frames[i][y][x]]);  				} else {  					if (frames[i][y][x] != last && colors[frames[i][y][x]]) { +						/* Normal Mode, send escape (because the color changed) */  						last = frames[i][y][x];  						printf("%s%s", colors[frames[i][y][x]], output);  					} else { +						/* Same color, just send the output characters */  						printf("%s", output);  					}  				}  			} +			/* End of row, send newline */  			if (y != MAX_ROW - 1)  				printf("\n");  		} +		/* Update frame crount */  		++i;  		if (!frames[i]) { +			/* Loop animation */  			i = 0;  		} +		/* Reset cursor */  		printf("\033[H"); +		/* Wait */  		usleep(90000);  	}  	return 0; | 
