keep accepting partial reads from client until client disconnects
int main() {
    int on = 1;

    User *user_list = NULL; // Create head of empty user list
    char *username = malloc(sizeof(char) * 32);
    setbuf(stdout, NULL); // stdout will not be buffered

    struct sockaddr_in *self = init_server_addr(PORT);
    int listenfd = set_up_server_socket(self, 5);
    int nbytes = 0;

    int status = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
                            (const char *) &on, sizeof(on));
    if (status == -1) {
        perror("setsockopt -- REUSEADDR");
    }

    while (1) {
        int fd = accept_connection(listenfd);
        if (fd < 0) {
            continue;
        }

        char buf[BUFSIZE] = {'\0'};
        int inbuf = 0;           // How many bytes currently in buf?
        int room = sizeof(buf);  // How many bytes remaining in buf?
        char *after = buf;       // Pointer to position after the data in buf

        nbytes = 0;
        while ((nbytes == read(fd, after, room)) > 0) {
            inbuf += nbytes;
            int where;
            // Account for partial reads by determining if a full
            // line has been read from the client.
            while ((where = find_network_newline(buf, inbuf)) > 0) {
                buf[where - 2] = '\0';
                printf("Full command: %s\n", buf);
                char *cmd_argv[INPUT_ARG_MAX_NUM];
                int cmd_argc = tokenize(buf, cmd_argv, fd);
                if (cmd_argc > 0 && process_args(cmd_argc, cmd_argv, &user_list, fd, username) == -1) {
                    break;
                }
                inbuf -= where;
                // Move characters after the full line to the front of buf.
                memmove(buf, &buf[where], inbuf);
            }
            // Prepare for next read.
            after = &(buf[inbuf]);
            room = BUFSIZE - inbuf;
        }
        close(fd);
        printf("Connection now closed\n");
    }

    free(self);
    close(listenfd);
    return 0;
}