uni

University stuff
git clone git://git.margiolis.net/uni.git
Log | Files | Refs | README | LICENSE

rps_client.c (7280B)


      1 #include <sys/socket.h>
      2 #include <sys/types.h>
      3 
      4 #include <arpa/inet.h>
      5 #include <netdb.h>
      6 #include <netinet/in.h>
      7 
      8 #include <err.h>
      9 #include <errno.h>
     10 #include <locale.h>
     11 #include <signal.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <unistd.h>
     16 
     17 #include "extern.h"
     18 
     19 #define NTDS	2
     20 
     21 struct command {
     22 	char name[CMD_LEN];
     23 	int (*func)(char *);
     24 };
     25 
     26 static int cmd_challenge(char *);
     27 static int cmd_accept(char *);
     28 static int cmd_msg(char *);
     29 static int cmd_list(char *);
     30 static int cmd_quit(char *);
     31 static int cmd_help(char *);
     32 static struct command *parse_command(char **);
     33 static void sighandler(int);
     34 static void usage(void);
     35 
     36 static struct command commands[] = {
     37 	{ "challenge",	cmd_challenge },/* /challenge <id> <rock|paper|scissor> */
     38 	{ "accept",	cmd_accept },	/* /accept <rock|paper|scissor> */
     39 	{ "msg",	cmd_msg },	/* /msg <message> */
     40 	{ "list",	cmd_list },	/* /list */
     41 	{ "quit",	cmd_quit },	/* /quit */
     42 	{ "help",	cmd_help },	/* /help */
     43 };
     44 
     45 static int ymax;
     46 static int xmax;
     47 static int fd;
     48 static volatile sig_atomic_t f_quit = 0;
     49 
     50 static int
     51 cmd_challenge(char *args)
     52 {
     53 	char cmd[CMD_LEN], *s;
     54 	int id, move;
     55 
     56 	if (args == NULL) {
     57 		warnx("usage: /challenge <id> <rock|paper|scissor>");
     58 		return (-1);
     59 	}
     60 	s = strchr(args, ' ');
     61 	if (s == NULL || *s == '\0') {
     62 		warnx("usage: /challenge <id> <rock|paper|scissor>");
     63 		return (-1);
     64 	}
     65 	while (*s == ' ')
     66 		s++;
     67 	if (*s == '\0') {
     68 		warnx("usage: /challenge <id> <rock|paper|scissor>");
     69 		return (-1);
     70 	}
     71 	if ((move = str2move(s)) < 0) {
     72 		warnx("invalid move: %s", s);
     73 		return (-1);
     74 	}
     75 
     76 	strlcpy(cmd, "challenge", sizeof(cmd));
     77 	if (send(fd, cmd, sizeof(cmd), 0) < 0) {
     78 		warn("send(challenge, cmd)");
     79 		return (-1);
     80 	}
     81 
     82 	id = strtol(args, NULL, 10);
     83 	if (errno == EINVAL || errno == ERANGE) {
     84 		warn("strtol(%s)", args);
     85 		return (-1);
     86 	}
     87 	if (send(fd, &id, sizeof(id), 0) < 0) {
     88 		warn("send(challenge, id)");
     89 		return (-1);
     90 	}
     91 
     92 	if (send(fd, &move, sizeof(move), 0) < 0) {
     93 		warn("send(challenge, move)");
     94 		return (-1);
     95 	}
     96 
     97 	return (0);
     98 }
     99 
    100 static int
    101 cmd_accept(char *args)
    102 {
    103 	char cmd[CMD_LEN];
    104 	int move;
    105 
    106 	if (args == NULL) {
    107 		warnx("usage: /challenge <rock|paper|scissor>");
    108 		return (-1);
    109 	}
    110 
    111 	strlcpy(cmd, "accept", sizeof(cmd));
    112 	if (send(fd, cmd, sizeof(cmd), 0) < 0) {
    113 		warn("send(accept, cmd)");
    114 		return (-1);
    115 	}
    116 
    117 	if ((move = str2move(args)) < 0) {
    118 		warnx("invalid move: %s", args);
    119 		return (-1);
    120 	}
    121 
    122 	if (send(fd, &move, sizeof(move), 0) < 0) {
    123 		warn("send(accept, move)");
    124 		return (-1);
    125 	}
    126 
    127 	return (0);
    128 }
    129 
    130 static int
    131 cmd_msg(char *args)
    132 {
    133 	char cmd[CMD_LEN];
    134 	size_t len;
    135 
    136 	if (args == NULL) {
    137 		warnx("usage: /msg <message>");
    138 		return (-1);
    139 	}
    140 
    141 	strlcpy(cmd, "msg", sizeof(cmd));
    142 	if (send(fd, cmd, sizeof(cmd), 0) < 0) {
    143 		warn("send(msg)");
    144 		return (-1);
    145 	}
    146 
    147 	len = strlen(args) + 1;
    148 	if (send(fd, &len, sizeof(len), 0) < 0) {
    149 		warn("send(msg, len)");
    150 		return (-1);
    151 	}
    152 
    153 	if (send(fd, args, len, 0) < 0) {
    154 		warn("send(msg, msg)");
    155 		return (-1);
    156 	}
    157 
    158 	return (0);
    159 }
    160 
    161 static int
    162 cmd_list(char *args)
    163 {
    164 	char cmd[CMD_LEN], buf[BUF_LEN];
    165 
    166 	strlcpy(cmd, "list", sizeof(cmd));
    167 	if (send(fd, cmd, sizeof(cmd), 0) < 0) {
    168 		warn("send(list)");
    169 		return (-1);
    170 	}
    171 
    172 	if (recv(fd, buf, sizeof(buf), 0) < 0) {
    173 		warn("recv(list)");
    174 		return (-1);
    175 	}
    176 	puts(buf);
    177 
    178 	return (0);
    179 }
    180 
    181 static int
    182 cmd_quit(char *args)
    183 {
    184 	char cmd[CMD_LEN];
    185 
    186 	f_quit = 1;
    187 
    188 	strlcpy(cmd, "quit", sizeof(cmd));
    189 	if (send(fd, cmd, sizeof(cmd), 0) < 0) {
    190 		warn("send(quit)");
    191 		return (-1);
    192 	}
    193 
    194 	return (0);
    195 }
    196 
    197 static int
    198 cmd_help(char *args)
    199 {
    200 	printf("Available commands:\n");
    201 	printf("/challenge <id|name> <rock|paper|scissor>\t\t"
    202 	    "Challenge a player\n");
    203 	printf("/accept <rock|paper|scisssor>\tAccept challenge\n");
    204 	printf("/msg <message>\t\t\tSend a message to the global chat\n");
    205 	printf("/quit\t\t\t\tQuit the game\n");
    206 	printf("/help\t\t\t\tShow this help message\n");
    207 
    208 	return (0);
    209 }
    210 
    211 static struct command *
    212 parse_command(char **args)
    213 {
    214 	char buf[BUF_LEN], *line, *cmd, *s;
    215 	int i;
    216 
    217 	printf("\r> ");
    218 	fgets(buf, sizeof(buf), stdin);
    219 	line = buf;
    220 
    221 	if (*line == '\n' || *line == '\0') {
    222 		warnx("empty command");
    223 		return (NULL);
    224 	}
    225 
    226 	/* Get rid of leading whitespaces. */
    227 	while (*line == ' ')
    228 		line++;
    229 
    230 	/* Get rid of trailing newlines. */
    231 	for (s = line; *s != '\0'; s++) {
    232 		if (*s != '\n')
    233 			continue;
    234 		*s = '\0';
    235 		break;
    236 	}
    237 
    238 	/* Commands must start with /. */
    239 	if (*line != '/') {
    240 		warnx("please enter a command");
    241 		return (NULL);
    242 	}
    243 	line++;
    244 
    245 	*args = strchr(line, ' ');
    246 	if (*args != NULL) {
    247 		while (**args == ' ')
    248 			(*args)++;
    249 	}
    250 	/* FIXME */
    251 	/*if (**args == '\0')*/
    252 		/**args = NULL;*/
    253 
    254 	cmd = strtok(line, " ");
    255 	if (cmd == NULL) {
    256 		warn("strtok() failed");
    257 		return (NULL);
    258 	}
    259 
    260 	/* Match command. */
    261 	for (i = 0; i < ARRLEN(commands); i++) {
    262 		if (strncmp(cmd, commands[i].name,
    263 		    strlen(commands[i].name)) == 0) {
    264 			if (*args != NULL)
    265 				*args = strdup(*args);
    266 			return (&commands[i]);
    267 		}
    268 	}
    269 	warnx("invalid command: %s", cmd);
    270 
    271 	return (NULL);
    272 }
    273 
    274 static void
    275 sighandler(int sig)
    276 {
    277 	f_quit = 1;
    278 }
    279 
    280 static void
    281 usage(void)
    282 {
    283 	fprintf(stderr, "usage: %1$s [-p port] <hostname|ipv4_addr>\n",
    284 	    getprogname());
    285 	exit(1);
    286 }
    287 
    288 int
    289 main(int argc, char *argv[])
    290 {
    291 	struct sockaddr_in sin;
    292 	struct hostent *hp;
    293 	struct sigaction sa;
    294 	struct command *cmd;
    295 	char *args;
    296 	char *s;
    297 	char nick[NAME_LEN];
    298 	int port = 9999;
    299 	int ch;
    300 
    301 	while ((ch = getopt(argc, argv, "p:")) != -1) {
    302 		switch (ch) {
    303 		case 'p':
    304 			if ((port = atoi(optarg)) < 1024)
    305 				errx(1, "cannot user port number < 1024");
    306 			break;
    307 		case '?':
    308 		default:
    309 			usage();
    310 		}
    311 	}
    312 	argc -= optind;
    313 	argv += optind;
    314 
    315 	if (argc < 1)
    316 		usage();
    317 	/*
    318 	 * Handle termination signals so we don't exit abnormally (i.e without
    319 	 * cleaning up resources).
    320 	 */
    321 	memset(&sa, 0, sizeof(sa));
    322 	sigfillset(&sa.sa_mask);
    323 	sa.sa_handler = sighandler;
    324 	if (sigaction(SIGINT, &sa, NULL) < 0)
    325 		err(1, "sigaction(SIGINT)");
    326 	if (sigaction(SIGTERM, &sa, NULL) < 0)
    327 		err(1, "sigaction(SIGTERM)");
    328 
    329 	if (!setlocale(LC_ALL, ""))
    330 		err(1, "setlocale");
    331 
    332 	/* Choose nickname. */
    333 	printf("\rEnter nickname (no whitespaces): ");
    334 	fgets(nick, sizeof(nick), stdin);
    335 	s = nick;
    336 
    337 	if (*s == '\n' || *s == '\0')
    338 		errx(1, "empty nickname");
    339 
    340 	/* Get rid of leading whitespaces. */
    341 	while (*s == ' ')
    342 		s++;
    343 
    344 	/* Get rid of trailing newlines and whitespaces. */
    345 	for (; *s != '\0'; s++) {
    346 		if (*s != '\n' && *s != ' ')
    347 			continue;
    348 		*s = '\0';
    349 		break;
    350 	}
    351 
    352 	/* Set up connection. */
    353 	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    354 		err(1, "socket(AF_INET)");
    355 	memset(&sin, 0, sizeof(sin));
    356 	sin.sin_family = AF_INET;
    357 	sin.sin_port = htons(port);
    358 	if (!inet_aton(*argv, &sin.sin_addr)) {
    359 		if ((hp = gethostbyname(*argv)) == NULL)
    360 			errx(1, "gethostbyname(%s) failed", *argv);
    361 		memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
    362 	}
    363 	if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    364 		err(1, "connect");
    365 
    366 	/* Send nickname to server */
    367 	if (send(fd, nick, sizeof(nick), 0) < 0)
    368 		err(1, "send(nick)");
    369 
    370 	/* Print list of available players. */
    371 	cmd_list(NULL);
    372 
    373 	/* Main game loop. */
    374 	for (;;) {
    375 		if (f_quit)
    376 			break;
    377 		if ((cmd = parse_command(&args)) == NULL)
    378 			continue;
    379 		if (cmd->func(args) < 0)
    380 			warnx("/%s(%s) failed\n", cmd->name, args);
    381 		if (args != NULL)
    382 			free(args);
    383 	}
    384 
    385 	close(fd);
    386 
    387 	return (0);
    388 }