Where are the segmentation faults
int is_target_line(const char *line);
Rule *init_rule(char *line);
void insert_rule(Rule **rules_head, Rule **new_rule);
void add_actions(Rule **last_rule_ptr, char *line);
void add_dependencies(Rule **rules_head, char *line);
Rule *find_rule(Rule **rules_ptr, char *name);

/* 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 *first_rule_ptr = NULL;
    Rule *last_rule_ptr = NULL;
    char str[MAXLINE];

    while (fgets(str, MAXLINE, fp) != NULL) {
        if (str[0] == ' ' || str[0] == '\t' || str[0] == '\n') {}
        else if (is_comment_or_empty(str)) {}
        else if (is_target_line(str)) {
                Rule *new_rule = init_rule(str);
                last_rule_ptr = new_rule;
                if (first_rule_ptr == NULL) {first_rule_ptr = new_rule;}
                else {insert_rule(&first_rule_ptr, &new_rule);}
        } else { // is action line
                add_actions(&last_rule_ptr, str);
        }
    }

    fseek(fp, 0, SEEK_SET);
    while (fgets(str, MAXLINE, fp) != NULL) {
        if (is_target_line(str)) {
                add_dependencies(&first_rule_ptr, str);
        }
    }

    return first_rule_ptr;
}
/* Return 1 if the line is a target line, as defined on the assignment handout.
 * Return 0 otherwise.
 */
int is_target_line(const char *line) {
    if (strstr(line, " : ") != NULL) {
        return 1;
    }
    return 0;
}

/* Initialize a new rule node with the specified target name
 * from line (in target line format). Initialize everything else to NULL.
 * Return a pointer to this new node.
 */
Rule *init_rule(char *line) {

    Rule *new_rule = malloc(sizeof(Rule));
    new_rule->dependencies = NULL;
    new_rule->actions = NULL;
    new_rule->next_rule = NULL;
    new_rule->target = NULL;

    char *ptr = strtok(line, " :");
    new_rule->target = malloc((sizeof(char) * strlen(ptr)) + 1);
    strcpy(new_rule->target, ptr);
    new_rule->target[strlen(ptr)] = '\0';

    return new_rule;
}

/* Add dependencies to target from line (in target line format).
 * Point each dependency to its corresponding rule node in the
 * rules list pointed to by rules_head.
 */
void add_dependencies(Rule **rules_head, char *line) {

    char *ptr = strtok((char *)line, " :"); // target name

    Rule *curr = *rules_head;
    while (curr->next_rule != NULL) {
        if (strcmp(curr->target, ptr) == 0) {break;}
        else {curr = curr->next_rule;}
    }

    ptr = strtok(NULL, " ");
    while (ptr != NULL) {
        Dependency *new_dep = malloc(sizeof(Dependency));
        new_dep->rule = NULL;
        new_dep->next_dep = NULL;
        new_dep->rule = find_rule(&curr, ptr);

        if (curr->dependencies == NULL) {
                curr->dependencies = new_dep;
                new_dep->next_dep = NULL;
        } else {
                new_dep->next_dep = curr->dependencies;
                curr->dependencies = new_dep;
        }
        ptr = strtok(NULL, " ");
    }
}

/* Return the rule node with the given target name
 * in the rules list pointed to by rules_ptr.
 * If no such rule exists, create it and return it.
 */
Rule *find_rule(Rule **rules_ptr, char *name) {
        Rule *curr = *rules_ptr;
        while(curr->next_rule != NULL) {
                if (strcmp(curr->target, name) == 0) {
                        break;
                }
                curr = curr->next_rule;
        }

        if (strcmp(curr->target, name) != 0) { // rule doesn't exist
                curr->next_rule = init_rule(name);
                curr = curr->next_rule;
        }

        return curr;
}

/* Insert the new rule to the tail of the rules list pointed to
 * by rules_head.
 *
 * Precondition: rules list pointed to by rules_head is not empty
 */
void insert_rule(Rule **rules_head, Rule **new_rule) {
    Rule *curr = *rules_head;
    while (curr->next_rule != NULL) {
            curr = curr->next_rule;
    }
    curr->next_rule = *new_rule;
}

/* Add actions in line (in action line format) to
 * the rule node pointed to by last_rule_ptr
 */
void add_actions(Rule **last_rule_ptr, char *line) {}