When I run this, with standard input:
"
add_user j
add_user k
add_user l
add_user m
delete_user k
add_user k
list_users
"
the standard output begins the infinitely print "k" over and over. Why is this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "friends.h"

#define INPUT_BUFFER_SIZE 256
#define INPUT_ARG_MAX_NUM 12
#define DELIM " \n"


/*
 * Print a formatted error message to stderr.
 */
void error(char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
}

/*
 * Read and process commands
 * Return:  -1 for quit command
 *          0 otherwise
 */
int process_args(int cmd_argc, char **cmd_argv, User **user_list_ptr) {
    User *user_list = *user_list_ptr;

    if (cmd_argc <= 0) {
        return 0;
    } else if (strcmp(cmd_argv[0], "quit") == 0 && cmd_argc == 1) {
        return -1;
    } else if (strcmp(cmd_argv[0], "add_user") == 0 && cmd_argc == 2) {
        switch (create_user(cmd_argv[1], user_list_ptr)) {
            case 1:
                error("user by this name already exists");
                break;
            case 2:
                error("username is too long");
                break;
        }
    } else if (strcmp(cmd_argv[0], "list_users") == 0 && cmd_argc == 1) {
        list_users(user_list);
    } else if (strcmp(cmd_argv[0], "update_pic") == 0 && cmd_argc == 3) {
        User *user = find_user(cmd_argv[1], user_list);
        if (user == NULL) {
            error("user not found");
        } else {
            switch (update_pic(user, cmd_argv[2])) {
                case 1:
                    error("file not found");
                    break;
                case 2:
                    error("filename too long");
                    break;
            }
       }
    } else if (strcmp(cmd_argv[0], "delete_user") == 0 && cmd_argc == 2) {
        if (delete_user(cmd_argv[1], user_list_ptr) == 1) {
            error("user by this name does not exist");
        }
    } else if (strcmp(cmd_argv[0], "make_friends") == 0 && cmd_argc == 3) {
        switch (make_friends(cmd_argv[1], cmd_argv[2], user_list)) {
            case 1:
                error("users are already friends");
                break;
            case 2:
                error("at least one user you entered has the max number of friends");
                break;
            case 3:
                error("you must enter two different users");
                break;
            case 4:
                error("at least one user you entered does not exist");
                break;
        }
    } else if (strcmp(cmd_argv[0], "post") == 0 && cmd_argc >= 4) {
        // first determine how long a string we need
        int space_needed = 0;
        for (int i = 3; i < cmd_argc; i++) {
            space_needed += strlen(cmd_argv[i]) + 1;
        }

        // allocate the space
        char *contents = malloc(space_needed);

        // copy in the bits to make a single string
        strcpy(contents, cmd_argv[3]);
        for (int i = 4; i < cmd_argc; i++) {
            strcat(contents, " ");
            strcat(contents, cmd_argv[i]);
        }

        User *author = find_user(cmd_argv[1], user_list);
        User *target = find_user(cmd_argv[2], user_list);
        switch (make_post(author, target, contents)) {
            case 1:
                error("the users are not friends");
                break;
            case 2:
                error("at least one user you entered does not exist");
                break;
        }
    } else if (strcmp(cmd_argv[0], "profile") == 0 && cmd_argc == 2) {
        User *user = find_user(cmd_argv[1], user_list);
        if (print_user(user) == 1) {
            error("user not found");
        }
    } else {
        error("Incorrect syntax");
    }
    return 0;
}


/*
 * Tokenize the string stored in cmd.
 * Return the number of tokens, and store the tokens in cmd_argv.
 */
int tokenize(char *cmd, char **cmd_argv) {
    int cmd_argc = 0;
    char *next_token = strtok(cmd, DELIM);
    while (next_token != NULL) {
        if (cmd_argc >= INPUT_ARG_MAX_NUM - 1) {
            error("too many arguments!");
            cmd_argc = 0;
            break;
        }
        cmd_argv[cmd_argc] = next_token;
        cmd_argc++;
        next_token = strtok(NULL, DELIM);
    }

    return cmd_argc;
}


int main(int argc, char* argv[]) {
    int batch_mode = (argc == 2);
    char input[INPUT_BUFFER_SIZE];
    FILE *input_stream;

    // Create the heads of the empty data structure
    User *user_list = NULL;

    if (batch_mode) {
        input_stream = fopen(argv[1], "r");
        if (input_stream == NULL) {
            perror("Error opening file");
            exit(1);
        }
    } else {
        // interactive mode
        input_stream = stdin;
    }

    printf("Welcome to FriendMe! (Local version)\nPlease type a command:\n> ");

    while (fgets(input, INPUT_BUFFER_SIZE, input_stream) != NULL) {
        // only echo the line in batch mode since in interactive mode the user
        // just typed the line
        if (batch_mode) {
            printf("%s", input);
        }

        char *cmd_argv[INPUT_ARG_MAX_NUM];
        int cmd_argc = tokenize(input, cmd_argv);

        if (cmd_argc > 0 && process_args(cmd_argc, cmd_argv, &user_list) == -1) {
            break; // can only reach if quit command was entered
        }

        printf("> ");
    }

    if (batch_mode) {
        fclose(input_stream);
    }

    // Delete all users. This should free all heap memory that was allocated
    // during the run of the program. Uncomment after you've implemented
    // deleting users.
    /*printf("Freeing all heap space.\n");
    while (user_list != NULL) {
        delete_user(user_list->name, &user_list);
    }*/
    return 0;
 }

/*
 * HELPER FUNCTION *******
 * Calculate the length of a friends list
 */
int num_friends(User **friends_list) {
    int friends = 0;

    // loop through all friends, count them by checking for null (remember, we intialized it as NULL)
    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (friends_list[i] != NULL) {
            friends++;
        } else {
            return friends;
        }
    }

    return friends;
}

/*
 * HELPER FUNCTION ********
 * Return if name is a friend in friend_list
 * 
 * Return:
 *      - 1 if they are friends
 *      - 0 otherwise
 */
int in_friend_list(const char *name, User **friend_list) {
    if (name == NULL || friend_list == NULL) {
        // error handling
        return 2;
    }

    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (friend_list[i] == NULL) {
            return 0;
        }
        if (strcmp((friend_list[i])->name, name) == 0) {
            return 1;
        }
    }
    return 0;
}

/*
 * HELPER FUNCTION ******** 
 * Deallocate the passed in user from memory
 */
void deallocate_user(User *user) {
    free(user);
}

/*
 * HELPER FUNCTION ******** 
 * Deletes the User from the Friend friend's list
 */
void delete_user_from_friend_arr(User *user, User *friend) {
    // step 1: find the index of friend array that user is at
    int i = 0;
    while (strcmp((friend->friends)[i]->name, user->name) != 0) {
        i++;
    }
    // index i is the index of that friend array. 

    // step 2: push back everything above that index by 1
    while (i < MAX_FRIENDS - 1) {
        (friend->friends)[i] = (friend->friends)[i + 1];
        i++;
    }

    // step 3: update the last index to be NULL
    (friend->friends)[MAX_FRIENDS - 1] = NULL;
}


/*
 * HELPER FUNCTION ******** 
 * Removes the user from all of its friends' friend arrays.
 */
void remove_from_all_friend_arr(User *user) {
    for (int i = 0; i < MAX_FRIENDS; i++) {
        // if this friend exists, remove this user from their friend array
        if ((user->friends)[i] != NULL) {
            delete_user_from_friend_arr(user, (user->friends)[i]);
            // don't have to do it the other way around because we will free this user later anyway
        }
    }
}

/*
 * Create a new user with the given name.  Insert it at the tail of the list
 * of users whose head is pointed to by *user_ptr_add.
 *
 * Return:
 *   - 0 if successful
 *   - 1 if a user by this name already exists in this list
 *   - 2 if the given name cannot fit in the 'name' array
 *       (don't forget about the null terminator)
 */
int create_user(const char *name, User **user_ptr_add) {
    if (name == NULL) {
        fprintf(stderr, "Cannot create user with empty name.\n");
    }

    if (strlen(name) > MAX_NAME - 1) {
        return 2;
    }
    
    User *new_user = malloc(sizeof(User));
    if (new_user == NULL) {
        fprintf(stderr, "Could not allocate space in memory.\n");
        return 3;
    }
    
    strncpy(new_user->name, name, MAX_NAME);
    new_user->name[MAX_NAME - 1] = '\0';
    for (int i = 0; i < MAX_FRIENDS; i++) {
        new_user->friends[i] = NULL; // initialize the array to NULL for all elements
    }
    // initialize all to NULL -- this will be our placeholder for "not set yet"
    //      -> we don't know what the initial value is at the memory location, so we set it as such.
    new_user->first_post = NULL;
    new_user->next = NULL;
    
    User *curr_user_ptr = *user_ptr_add;
    // if this is the first user being added (user_ptr_add is NULL)
    if (curr_user_ptr == NULL) {
        *user_ptr_add = new_user;
        return 0;
    }

    // loop through linkedlist until the end, to add user
    while (curr_user_ptr->next != NULL) {
        // if the user is already in this list, return 1
        if (strcmp(curr_user_ptr->name, name) == 0) {
            return 1;
        }
        curr_user_ptr = curr_user_ptr->next;
    }
    curr_user_ptr->next = new_user;

    return 0;
}


/*
 * Return a pointer to the user with this name in
 * the list starting with head. Return NULL if no such user exists.
 *
 * NOTE: You'll likely need to cast a (const User *) to a (User *)
 * to satisfy the prototype without warnings.
 */
User *find_user(const char *name, const User *head) {
    while (head != NULL) {
        if (strcmp(head->name, name) == 0) {
            return (User *) head;
        }
        head = head->next;
    }
    return NULL;
}


/*
 * Print the usernames of all users in the list starting at curr.
 * Names should be printed to standard output, one per line.
 */
void list_users(const User *curr) {
    printf("User List\n");
    while (curr != NULL) {
        printf("	%s\n", curr->name);
        curr = curr->next;
    }
}


/*
 * Change the filename for the profile pic of the given user.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if the file does not exist or cannot be opened.
 *   - 2 if the filename is too long.
 */
int update_pic(User *user, const char *filename) {
    if (filename == NULL) {
        return 1;
    }

    if (strlen(filename) > MAX_NAME - 1) { // - 1 b/c null terminator!
        return 2;
    }

    FILE *profile_pic = fopen(filename, "r");
    if (profile_pic == NULL) {
        return 1;
    }
    int closed = fclose(profile_pic);
    // ERROR: file could not be closed
    if (closed != 0) {
        return 1;
    }
    strncpy(user->profile_pic, filename, MAX_NAME);
    user->profile_pic[MAX_NAME - 1] = '\0';

    return 0;
}


/*
 * Make two users friends with each other.  This is symmetric - a pointer to
 * each user must be stored in the 'friends' array of the other.
 *
 * New friends must be added in the first empty spot in the 'friends' array.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if the two users are already friends.
 *   - 2 if the users are not already friends, but at least one already has
 *     MAX_FRIENDS friends.
 *   - 3 if the same user is passed in twice.
 *   - 4 if at least one user does not exist.
 *
 * Do not modify either user if the result is a failure.
 * NOTE: If multiple errors apply, return the *largest* error code that applies.
 */
int make_friends(const char *name1, const char *name2, User *head) {
    if (strcmp(name1, name2) == 0) {
        // because we want to return largest error code, check if this user does not exist
        User *user = find_user(name1, head);
        if (user == NULL) {
            return 4;
        }
        return 3;
    }
    User *user1 = find_user(name1, head);
    User *user2 = find_user(name2, head);
    if (user1 == NULL || user2 == NULL) {
        // at least one does not exist
        return 4;
    }
    
    // check if friends
    // make helper function: are_friends
    if (in_friend_list(name2, user1->friends) == 1 || in_friend_list(name1, user2->friends) == 1) {
        return 1;
    }

    // if not friends, check if at least one has MAX_FRIENDS
    if (num_friends(user1->friends) > MAX_FRIENDS || num_friends(user2->friends) > MAX_FRIENDS) {
        return 2;
    }

    // otherwise, make them friends!
    (user1->friends)[num_friends(user1->friends)] = user2;
    (user2->friends)[num_friends(user2->friends)] = user1;


    return 0;
}


/*
 * Print a user profile.
 * For an example of the required output format, see the example output
 * linked from the handout.
 * Return:
 *   - 0 on success.
 *   - 1 if the user is NULL.
 */
int print_user(const User *user) {
    if (user == NULL) {
        return 1;
    }
    // TODO: print profile picture
    FILE *profile_pic;
    profile_pic = fopen(user->profile_pic, "r");
    if (profile_pic != NULL) {
        // TODO: revisit how you would go about printing profile picture!! this sets a hard limit for size
        char line[80];
        while (fgets(line, 81, profile_pic) != NULL) {
            printf("%s", line);
        }

        int err = fclose(profile_pic);
        if (err != 0) {
            fprintf(stderr, "Closing profile picture file failed.\n");
            return 1;
        }
        printf("\n");
    }

    printf("Name: %s\n", user->name);
    printf("------------------------------------------\n");
    printf("Friends:\n");
    int i = 0;
    while (user->friends[i] != NULL) {
        printf("%s\n", user->friends[i]->name);
        i++;
    }
    printf("------------------------------------------\n");
    printf("Posts:\n");
    Post *curr_post = user->first_post;
    while (curr_post != NULL) {
        printf("From: %s\n", curr_post->author);
        // use ctime to print date in readable format
        printf("Date: %s\n", ctime(curr_post->date));
        printf("%s\n", curr_post->contents);

        // if there is another post, print formatting to match sample_output.txt
        if (curr_post->next != NULL) {
            printf("\n===\n\n");
        }

        curr_post = curr_post->next;
    }
    printf("------------------------------------------\n");
    return 0;
}


/*
 * Make a new post from 'author' to the 'target' user,
 * containing the given contents, IF the users are friends.
 *
 * Insert the new post at the *front* of the user's list of posts.
 *
 * 'contents' is a pointer to heap-allocated memory - you do not need
 * to allocate more memory to store the contents of the post.
 *
 * Return:
 *   - 0 on success
 *   - 1 if users exist but are not friends
 *   - 2 if either User pointer is NULL
 */
int make_post(const User *author, User *target, char *contents) {
    // if one of the pointers are null
    if (author == NULL || target == NULL) {
        return 2;
    }
    // users are not friends
    if (in_friend_list(target->name, ((User *) author)->friends) == 0 || in_friend_list(author->name, target->friends) == 0) {
        return 1;
    }

    // insert the new post in the FRONT of target's list of posts
    Post *new_post = malloc(sizeof(Post));
    if (new_post == NULL) {
        fprintf(stderr, "Could not allocate space in memory.\n");
        return 3;
    }
    new_post->contents = contents;
    strncpy(new_post->author, author->name, MAX_NAME);
    new_post->author[31] = '\0';
    
    time_t *curr_date = malloc(sizeof(time_t));
    // update curr_date to current time
    time(curr_date);
    new_post->date = curr_date;
    // initialize the new_post's next attribute to be NULL
    new_post->next = NULL;

    
    // update pointers to insert new post to front of list
    // THESE CAUSE SEG FAULTS
    if (target->first_post == NULL) {
        target->first_post = new_post;
        return 0;
    }

    new_post->next = target->first_post->next;
    target->first_post->next = new_post;
    return 0;
}


/*
 * From the list pointed to by *user_ptr_del, delete the user
 * with the given name.
 * Remove the deleted user from any lists of friends.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if a user with this name does not exist.
 */
int delete_user(const char *name, User **user_ptr_del) {
    User *user_ptr_curr = *user_ptr_del;
    if (user_ptr_curr == NULL) {
        return 1;
    }
    if (strcmp(user_ptr_curr->name, name) == 0) {
        // case: there the user we want to delete is at the head of the list
        
        // step 2: delete it from all of its friends arrays
        remove_from_all_friend_arr(user_ptr_curr);
        // note: only need to remove from the friend array, since our array will be freed

        
        // step 3: remove it from the pointer
        // keep track of the user we want to deallocate, as in the next line we would lose it
        User **user_to_deallocate = &user_ptr_curr;
        //  remove the first user from the friends list
        *user_ptr_del = user_ptr_curr->next;

        // step 4: free all posts on the users' timeline

        // step 5: free all of its allocated memory
        deallocate_user(*user_to_deallocate);
        return 0;
    }
    // loop through the user list until either you reach the end, or find the user with the given name
    while (user_ptr_curr->next != NULL && strcmp(user_ptr_curr->next->name, name) != 0) {
        user_ptr_curr = user_ptr_curr->next;
    }
    // case: you looped through the entire user list and did not find the user with such a name
    if (user_ptr_curr->next == NULL) {
        return 1;
    }
    // otherwise, user_ptr_curr->next is the pointer to the user to be deleted.
    // step 2: delete it from all of its friends arrays
    remove_from_all_friend_arr(user_ptr_curr);
        
    // step 3: remove it from the pointer
    // TODO: fix this. right now it sets the head to be the user we want to delete, instead of skipping over them
    // keep track of the user we want to deallocate, as in the next line we would lose it
    User **user_to_deallocate = &(user_ptr_curr->next);
    // skip the user we are removing in the list
    user_ptr_curr->next = (user_ptr_curr->next)->next;

    // step 4: free all posts on the users' timeline

    // step 5: free all of its allocated memory
    deallocate_user(*user_to_deallocate);

    return 0;
}
