How to fix segmentation fault?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pmake.h"


/* Return a pointer to the rule with the given target. If the target doesn't exist, return null.
 */
Rule *find_rule(Rule **rule_head, char target[MAXLINE]) {
    Rule *curr_rule = *rule_head;
    while (curr_rule != NULL) {
        if (strcmp(curr_rule->target, target) == 0) {
            return curr_rule;
        }
        curr_rule = curr_rule->next_rule;
    }
    return NULL;
}


/* Create a new rule with the given target. Insert it at the tail of the list
 * of rules whose head is pointed to by rule_head. Return a pointer to the created rule.
 */
Rule *create_rule(Rule **rule_head, char target[MAXLINE]) {
    // Iterate to end of rules
    Rule *prev = NULL;
    Rule *curr = *rule_head;
    while (curr != NULL) {
        prev = curr;
        curr = curr->next_rule;
    }

    // Create the rule
    Rule *new_rule = malloc(sizeof(Rule));
    new_rule->target = malloc((strlen(target) + 1) * sizeof(char));
    strcpy(new_rule->target, target);
    new_rule->dependencies = NULL;
    new_rule->actions = NULL;
    new_rule->next_rule = NULL;

    // Link the rule
    if (rule_head == NULL) {
        rule_head[0] = new_rule;
    } else {
        prev->next_rule = new_rule;
    }

    return new_rule;
}


/* Search for the rule with the given target, where the list of rules
 * has their head pointed at rule_head and add the dependency with corr_rule.
 * Precondition:
 *   - the rule with the given target exists
 */
void add_dependency(Rule **rule_head, char target[MAXLINE], Rule *corr_rule) {
    // Find rule
    Rule *curr_rule = *rule_head;
    while (strcmp(curr_rule->target, target) != 0) {
        curr_rule = curr_rule->next_rule;
    }

    // Create the dependency
    Dependency *new_dep = malloc(sizeof(Dependency));
    new_dep->rule = corr_rule;
    new_dep->next_dep = NULL;

    // Link the dependency
    Dependency *prev_dep = NULL;
    Dependency *curr_dep = curr_rule->dependencies;
    while (curr_dep != NULL) {
        prev_dep = curr_dep;
        curr_dep = curr_dep->next_dep;
    }

    if (curr_rule->dependencies == NULL) {
        curr_rule->dependencies = new_dep;
    } else {
        prev_dep->next_dep = new_dep;
    }
}


/* Search for the rule with the given target, where the list of rules
 * has their head pointed at rule_head and link an action with args
 * to the rule.
 * Precondition:
 *   - the rule with the given target exists
 */
void add_action(Rule **rule_head, char target[MAXLINE], char **args) {
    // Find Rule
    Rule *curr_rule = *rule_head;
    while (strcmp(curr_rule->target, target) != 0) {
        curr_rule = curr_rule->next_rule;
    }

    // Create the action
    Action *new_action = malloc(sizeof(Action));
    new_action->args = args;
    new_action->next_act = NULL;

    // Link the action
    Action *prev_action = NULL;
    Action *curr_action = curr_rule->actions;
    while (curr_action != NULL) {
        prev_action = curr_action;
        curr_action = curr_action->next_act;
    }

    if (curr_rule->actions == NULL) {
        curr_rule->actions = new_action;
    } else {
        prev_action->next_act = new_action;
    }
}


/* Parse through the given (target) line and create and link the necessary dependencies and targets.
 * Return a pointer to the rule object with the given target in line
 */
Rule *parse_target_line(Rule **rule_head, char line[MAXLINE]) {
    int i = 0;
    int j;
    char target[MAXLINE];
    char dep[MAXLINE];

    while (line[i] != ' ') {
        target[i] = line[i]; // TODO: line seems to be characters generated from reading binary file
        i++;
    }
    target[i] = '\0';

    Rule *rule;
    if (line[i+2] == '\n') { // no dependencies
        if (find_rule(rule_head, target) == NULL) { // target doesn't exist
            rule = create_rule(rule_head, target);
        } else {
            rule = find_rule(rule_head, target);
        }
        return rule;
    }

    // Move index to first dependency
    i += 3;

    while (line[i] != '\n') { // Iterate over line
        // Initialize index for dependency
        j = 0;

        // Iterate over dependency
        while (line[i] != ' ' && line[i] != '\n') {
            dep[j] = line[i];
            j++;
            i++;
        }
        dep[j] = '\0';

        if (find_rule(rule_head, target) == NULL) { // target doesn't exist
            create_rule(rule_head, target);
        }

        Rule *corr_rule = find_rule(rule_head, dep);
        if (corr_rule == NULL) { // corr_rule doesn't exist
            corr_rule = create_rule(rule_head, dep);
        }
        add_dependency(rule_head, target, corr_rule);

        // Move index to next dependency
        if (line[i] != '\n') {
            i++;
        } else {
            break;
        }
    }

    return find_rule(rule_head, target);
}


/* Parse through the given (action) line and create and link the necessary actions and rules.
 */
void parse_action_line(Rule *rule, char line[MAXLINE]) {
    char **args = malloc(MAXLINE * sizeof(char *));

    char word[MAXLINE];
    int num_words = 0;
    int i = 1; // first index is '\t'
    int k;

    while (i != '\n') { // iterate over line
        k = 0;

        // Iterate over words in action
        while (line[i] != ' ' && line[i] != '\n') {
            word[k] = line[i];
            k++;
            i++;
        }
        word[k] = '\0';

        args[num_words] = malloc(sizeof(char) * (strlen(word) + 1));
        strcpy(args[num_words], word);

        num_words++;

        if (line[i] == ' ') {
            i++; // Move index to the next word
        }
    }
    args[num_words] = malloc(sizeof(NULL));
    args[num_words] = NULL;

    // Create action
    Action *new_action = malloc(sizeof(Action));
    new_action->args = args;
    new_action->next_act = NULL;

    // Link action to rule
    Action *prev_action = NULL;
    Action *curr_action = rule->actions;
    while (curr_action != NULL) {
        prev_action = curr_action;
        curr_action = curr_action->next_act;
    }

    if (rule->actions == NULL) {
        rule->actions = new_action;
    } else {
        prev_action->next_act = new_action;
    }
}


/* Read from the open file fp, and create the linked data structure
   that represents the Makefile contained in the file.
   See the top of pmake.h for the specification of Makefile contents.
 */
Rule *parse_file(FILE *fp) {
    // Implement this function and remove the stubbed return statement below.
    Rule *rule_head = NULL;
    char line[MAXLINE];
    line[0] = '\0';
    Rule *rule;

    while (fgets(line, MAXLINE, fp) != NULL) {
        if (is_comment_or_empty(line)) {
            continue;
        } else if (line[0] != ' ' && line[0] != '\t') { // target line
            printf("%s\n", line);
            rule = parse_target_line(&rule_head, line);
        } else { // action line
            parse_action_line(rule, line);
        }
    }
//    char target[MAXLINE];
//
//    Rule *curr_rule = new_rule;
//    while (fgets(line, max_line, fp) != NULL) {
//        if (line[0] != ' ' && line[0] != '\t') {
//            int i = 0;
//            while (line[i] != ' ') {
//                target[i] = line[i];
//                i++;
//            }
//            target[i] = '\0';
//
//            // initialize target attribute for curr_rule
//            curr_rule->target = malloc(strlen(target) + 1);
//            strcpy(curr_rule->target, target);
//
//            // initialize actions attribute to NULL
//            curr_rule->actions = NULL;
//
//            // initialize next_rule attribute to NULL
//            curr_rule->next_rule = NULL;
//
//            // initialize dependencies attribute to NULL
//            curr_rule->dependencies = NULL;
//
//            if (line[i] == '\n' || line[i+1] == '\n' || line[i+2] == '\n' || line[i+3] == '\n') {
//                // there are no dependencies so don't iterate through loop
//            }
//
//            i += 3; // move index to first dependency
//            curr_rule->dependencies = malloc(sizeof(Dependency));
//
//            Dependency *curr_dependency = new_rule->dependencies;
//
//            // iterate through dependencies
//            while (line[i] != '\n') {
//                // initialize new dependency
//                curr_dependency->rule = malloc(sizeof(Rule));
//                curr_dependency->next_dep = NULL;
//
//                // initialize new rule associated with new dependency
//                curr_rule->dependencies = curr_dependency->rule;
//                curr_rule->next_rule = curr_dependency->rule;
//                curr_rule = curr_rule->next_rule;
//
//                int j = 0;
//                char dependency[MAXLINE];
//
//                // initialize the dependency's corresponding Rule
//                while (line[i] != '\n' && line[i] != ' ') {
//                    dependency[j] = line[i];
//                    j++;
//                    i++;
//                }
//            }
//        }



//        for (int i = 0 ; i < max_line ; i++) {
//            if (line[i] == ' ') {
//                line[i] = '\0';
//            } else if (line[i] == '\n') {
//                line[i] = '\0';
//                break;
//            }
//        }
//    }
    return rule_head;
}


/******************************************************************************
 * These helper functions are provided for you. Do not modify them.
 *****************************************************************************/
/* Print the list of actions */
void print_actions(Action *act) {
    while(act != NULL) {
        if(act->args == NULL) {
            fprintf(stderr, "ERROR: action with NULL args\n");
            act = act->next_act;
            continue;
        }
        printf("\t");

        int i = 0;
        while(act->args[i] != NULL) {
            printf("%s ", act->args[i]) ;
            i++;
        }
        printf("\n");
        act = act->next_act;
    }
}

/* Print the list of rules to stdout in makefile format. If the output
   of print_rules is saved to a file, it should be possible to use it to
   run make correctly.
 */
void print_rules(Rule *rules){
    Rule *cur = rules;

    while (cur != NULL) {
        if (cur->dependencies || cur->actions) {
            // Print target
            printf("%s : ", cur->target);

            // Print dependencies
            Dependency *dep = cur->dependencies;
            while (dep != NULL){
                if(dep->rule->target == NULL) {
                    fprintf(stderr, "ERROR: dependency with NULL rule\n");
                }
                printf("%s ", dep->rule->target);
                dep = dep->next_dep;
            }
            printf("\n");

            // Print actions
            print_actions(cur->actions);
        }
        cur = cur->next_rule;
    }
}


/* Return 1 if the line is a comment line, as defined on the assignment handout.
   Return 0 otherwise.
 */
int is_comment_or_empty(const char *line) {
    for (int i = 0; i < strlen(line); i++){
        if (line[i] == '#') {
            return 1;
        }
        if (line[i] != '\t' && line[i] != ' ') {
            return 0;
        }
    }
    return 1;
}

/* Convert an array of args to a single space-separated string in buffer.
   Returns buffer.  Note that memory for args and buffer should be allocted
   by the caller.
 */
char *args_to_string(char **args, char *buffer, int size) {
    buffer[0] = '\0';
    int i = 0;
    while (args[i] != NULL) {
        strncat(buffer, args[i], size - strlen(buffer));
        strncat(buffer, " ", size - strlen(buffer));
        i++;
    }
    return buffer;
}
