├── .gitignore ├── Makefile ├── disk_utility.c ├── disk_utility.h ├── input.c ├── input.h ├── link_utility.c ├── link_utility.h ├── main_utility.c ├── main_utility.h ├── node_math.c ├── node_math.h ├── node_utility.c ├── node_utility.h ├── readme.md ├── reply.c ├── structures.h ├── tree_utility.c └── tree_utility.h /.gitignore: -------------------------------------------------------------------------------- 1 | node_test.js 2 | a.out 3 | *.txt 4 | *.gph 5 | *.o 6 | .*.mk 7 | reply 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -g 2 | 3 | # Be super strict about everything 4 | CFLAGS += -std=c99 -Werror -Wall -Wextra -pedantic -O2 5 | CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 6 | 7 | # Automatically sort out header dependencies 8 | CPPFLAGS += -MD -MF $(patsubst %.o,.%.mk,$@) -MP 9 | -include $(patsubst %.o,.%.mk,$(obj)) 10 | 11 | objs = \ 12 | disk_utility.o \ 13 | input.o \ 14 | link_utility.o \ 15 | main_utility.o \ 16 | node_math.o \ 17 | node_utility.o \ 18 | reply.o \ 19 | tree_utility.o 20 | 21 | all: reply 22 | 23 | reply: $(objs) 24 | 25 | clean: 26 | rm -f -- .*.mk *.o reply 27 | 28 | .PHONY: all clean 29 | .DELETE_ON_ERROR: 30 | -------------------------------------------------------------------------------- /disk_utility.c: -------------------------------------------------------------------------------- 1 | #include "disk_utility.h" 2 | 3 | #include /* for fopen(3), fclose(3), fprintf(3), sscanf(3), and fgets(3) */ 4 | #include /* for strcat(3) and strcmp(3) */ 5 | 6 | #include "node_utility.h" /* for get_node_by_id() and write_data() */ 7 | #include "link_utility.h" /* for write_link_data() */ 8 | #include "input.h" /* for process_input() */ 9 | 10 | void save_to_disk(struct Graph *g, char *filename){ 11 | int n = 0; 12 | int l = 0; 13 | char *name_a, *name_b; 14 | FILE * fp; 15 | strcat(filename, ".gph"); 16 | fp = fopen(filename, "w+"); 17 | while(n < g->num_nodes){ 18 | fprintf(fp, "new %s\n", g->nodes[n].name); 19 | if(strcmp(g->nodes[n].data, "NULL") != 0){ 20 | fprintf(fp, "wn %s\n%s\n", g->nodes[n].name, g->nodes[n].data); 21 | } 22 | n++; 23 | } 24 | while(l < g->num_links){ 25 | name_a = get_node_by_id(g->links[l].source, g)->name; 26 | name_b = get_node_by_id(g->links[l].target, g)->name; 27 | fprintf(fp, "link %s %s\n", name_a, name_b); 28 | if(strcmp(g->links[l].data, "NULL") != 0){ 29 | fprintf(fp, "wl %s %s\n%s\n", name_a, name_b, g->links[l].data); 30 | } 31 | l++; 32 | } 33 | fclose(fp); 34 | } 35 | 36 | void read_from_disk(char *filename, struct Graph *g){ 37 | FILE * fp; 38 | char line[100]; 39 | char name_a[100]; 40 | char name_b[100]; 41 | char type[5]; 42 | strcat(filename, ".gph"); 43 | fp = fopen(filename, "r"); 44 | while (fgets(line, 100, fp) != NULL){ 45 | if(line[0] == 'w' && line[1] == 'n'){ 46 | sscanf(line, "%s %s", type, name_a); 47 | fgets(line, 100, fp); 48 | write_data(line, name_a, g); 49 | } 50 | else if(line[0] == 'w' && line[1] == 'l'){ 51 | sscanf(line, "%s %s %s", type, name_a, name_b); 52 | fgets(line, 100, fp); 53 | write_link_data(line, name_a, name_b, g); 54 | } else { 55 | process_input(line, g); 56 | } 57 | } 58 | fclose(fp); 59 | } 60 | -------------------------------------------------------------------------------- /disk_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _DISK_UTILITY_H 2 | #define _DISK_UTILITY_H 3 | 4 | #include "structures.h" 5 | 6 | void save_to_disk(struct Graph *g, char *filename); 7 | void read_from_disk(char *filename, struct Graph *g); 8 | 9 | #endif /* _DISK_UTILITY_H */ 10 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | #include "input.h" 2 | 3 | #include /* for printf(3) and sscanf(3) */ 4 | #include /* for free(3) and strtol(3) */ 5 | #include /* for strcmp(3) */ 6 | 7 | #include "disk_utility.h" /* for read_from_disk() and save_to_disk() */ 8 | #include "link_utility.h" /* for a bunch of things */ 9 | #include "node_utility.h" /* for a bunch of things */ 10 | #include "node_math.h" /* for {add,subtract,multiply,divide}_nodes() */ 11 | 12 | void process_input(char *input, struct Graph *g){ 13 | char name_a[50]; 14 | char name_b[50]; 15 | char type[5]; 16 | int index = 0; 17 | while(input[index] != ' ' && input[index] != '\n'){ 18 | type[index] = input[index]; 19 | index++; 20 | if(index == 5 && input[index+1] != ' ' && input[index+1] != '\n'){ 21 | printf("{ error: \"Command longer than four characters.\" }\n"); 22 | return; 23 | } 24 | } 25 | if(input[index] == ' ' && input[index+1] != '\n'){ 26 | index++; 27 | int name_a_index = 0; 28 | while(input[index] != ' ' && input[index] != '\n'){ 29 | name_a[name_a_index] = input[index]; 30 | index++; 31 | name_a_index++; 32 | if(name_a_index == 50 && input[index+1] != ' ' && input[index+1] != '\n'){ 33 | printf("{ error: \"First command parameter longer than fifty characters.\" }\n"); 34 | return; 35 | } 36 | } 37 | } 38 | if(input[index] == ' '){ 39 | index++; 40 | int name_b_index = 0; 41 | while(input[index] != ' ' && input[index] != '\n'){ 42 | name_b[name_b_index] = input[index]; 43 | index++; 44 | name_b_index++; 45 | if(name_b_index == 50 && input[index+1] != ' ' && input[index+1] != '\n'){ 46 | printf("{ error: \"Second command parameter longer than fifty characters.\" }\n"); 47 | return; 48 | } 49 | } 50 | } 51 | 52 | sscanf(input, "%s %s %s", type, name_a, name_b); 53 | if(!strcmp(type,"new")){ 54 | add_new_node(name_a, g); 55 | } 56 | else if(!strcmp(type,"link")){ 57 | add_new_link(name_a, name_b, g); 58 | } 59 | else if(!strcmp(type,"out")){ 60 | print_outgoing_nodes(name_a, g); 61 | } 62 | else if(!strcmp(type,"in")){ 63 | print_incoming_nodes(name_a, g); 64 | } 65 | else if(!strcmp(type,"wn")){ 66 | write_data_prompt(name_a, g); 67 | } 68 | else if(!strcmp(type,"rn")){ 69 | read_data(name_a, g); 70 | } 71 | else if(!strcmp(type, "all")){ 72 | print_graph(g); 73 | } 74 | else if(!strcmp(type,"wl")){ 75 | write_link_data_prompt(name_a, name_b, g); 76 | } 77 | else if(!strcmp(type,"rl")){ 78 | read_link_data(name_a, name_b, g); 79 | } 80 | else if(!strcmp(type, "path")){ 81 | // printf("Hey"); 82 | test_for_path(name_a, name_b, g); 83 | } 84 | else if(!strcmp(type, "save")){ 85 | save_to_disk(g, name_a); 86 | } 87 | else if(!strcmp(type, "load")){ 88 | read_from_disk(name_a, g); 89 | } 90 | else if(!strcmp(type, "getl")){ 91 | get_links(g); 92 | // all links (node pairs) with X data 93 | } 94 | else if(!strcmp(type, "getn")){ 95 | get_nodes(g); 96 | // all nodes with X data 97 | } 98 | else if(!strcmp(type, "add")){ 99 | add_nodes(name_a, name_b, g); 100 | } 101 | else if(!strcmp(type, "div")){ 102 | divide_nodes(name_a, name_b, g); 103 | } 104 | else if(!strcmp(type, "sub")){ 105 | subtract_nodes(name_a, name_b, g); 106 | } 107 | else if(!strcmp(type, "mult")){ 108 | multiply_nodes(name_a, name_b, g); 109 | } 110 | else if(!strcmp(type, "cmd")){ 111 | run_command(name_a, g); 112 | } 113 | else if(!strcmp(type, "id")){ 114 | long long_val; 115 | long_val = strtol(name_a, NULL, 10); 116 | unsigned int id = (unsigned int) long_val; 117 | struct Node *node; 118 | node = get_node_by_id(id, g); 119 | if(!strcmp(node->name, "NULL")){ 120 | printf("{ \"error\": \"No node with id %d\" }\n", id); 121 | free(node->name); 122 | free(node); 123 | } else { 124 | printf("{ \"id: %d, \"name\": \"%s\", \"data\": \"%s\" }\n", id, node->name, node->data); 125 | } 126 | } 127 | else if(!strcmp(type, "help")){ 128 | printf(" new a creates new node with label \"a\"\n"); 129 | printf(" link a b creates a link between nodes a and b, creating them if they don't exist\n"); 130 | printf(" id X get node with id X\n"); 131 | printf(" wn a write data to node with label \"a\"\n"); 132 | printf(" wl a b write data to link connecting nodes a and b\n"); 133 | printf(" in a print links where a is the target\n"); 134 | printf(" out a print links where a is the source\n"); 135 | printf(" getl get all links with specific data\n"); 136 | printf(" getn get all nodes with specific data\n"); 137 | printf(" path a b finds path from a to b, or returns false is path doesn't exist\n"); 138 | printf(" all prints all nodes and links in the graph\n"); 139 | printf(" add a b returns the sum of the data values of a and b\n"); 140 | printf(" sub a b returns the difference of the data values of a and b\n"); 141 | printf(" div a b returns the quotient of the data values of a and b\n"); 142 | printf(" mult a b returns the product of the data values of a and b\n"); 143 | printf(" cmd a runs the data value of node a as a command\n"); 144 | } else { 145 | printf("{ error: \"Unknown command.\" }\n"); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /input.h: -------------------------------------------------------------------------------- 1 | #ifndef _INPUT_H 2 | #define _INPUT_H 3 | 4 | #include "structures.h" 5 | 6 | void process_input(char *input, struct Graph *g); 7 | 8 | #endif /* _INPUT_H */ 9 | -------------------------------------------------------------------------------- /link_utility.c: -------------------------------------------------------------------------------- 1 | #include "link_utility.h" 2 | 3 | #include /* for printf(3) */ 4 | #include /* for malloc(3), realloc(3), and free(3) */ 5 | #include /* for strcpy(3), strlen(3), and strcmp(3) */ 6 | 7 | #include "main_utility.h" /* for get_line() */ 8 | #include "node_utility.h" /* for get_id_by_name() and new_or_existing_id() */ 9 | 10 | void write_link_data(char *line, char *name_a, char *name_b, struct Graph *g){ 11 | int len = strlen(line); 12 | if(line[len-1] == '\n'){ 13 | line[len-1] = 0; 14 | } 15 | int i = 0, src, tgt; 16 | src = new_or_existing_id(name_a, g); 17 | tgt = new_or_existing_id(name_b, g); 18 | while(i < g->num_links){ 19 | if(src == g->links[i].source && tgt == g->links[i].target){ 20 | g->links[i].data = malloc(len+1); 21 | strcpy(g->links[i].data, line); 22 | break; 23 | } 24 | i++; 25 | } 26 | } 27 | 28 | void write_link_data_prompt(char *name_a, char *name_b, struct Graph *g){ 29 | if(!node_exists(name_a, g)){ 30 | printf("{ error: \"No node with name %s.\" }\n", name_a); 31 | } 32 | else if(!node_exists(name_b, g)){ 33 | printf("{ error: \"No node with name %s.\" }\n", name_b); 34 | } else { 35 | char *line; 36 | printf("[(%s->%s).data]> ", name_a, name_b); 37 | line = get_line(); 38 | write_link_data(line, name_a, name_b, g); 39 | free(line); 40 | } 41 | } 42 | 43 | void read_link_data(char *name_a, char *name_b, struct Graph *g){ 44 | int i = 0, src, tgt; 45 | src = new_or_existing_id(name_a, g); 46 | tgt = new_or_existing_id(name_b, g); 47 | while(i < g->num_links){ 48 | if(src == g->links[i].source && tgt == g->links[i].target){ 49 | printf("{ \"source\": \"%s\", \"target\": \"%s\", \"data\": \"%s\" }\n", name_a, name_b, g->links[i].data); 50 | break; 51 | } 52 | i++; 53 | } 54 | } 55 | 56 | void add_new_link(char *name_a, char *name_b, struct Graph *g){ 57 | int source = new_or_existing_id(name_a, g); 58 | int target = new_or_existing_id(name_b, g); 59 | struct Link new_link; 60 | new_link.source = source; 61 | new_link.target = target; 62 | new_link.data = "NULL"; 63 | g->num_links++; 64 | g->links[g->num_links-1] = new_link; 65 | } 66 | 67 | void get_links(struct Graph *g){ 68 | char *line; 69 | printf("[link.data]> "); 70 | line = get_line(); 71 | int len = strlen(line); 72 | if(line[len-1] == '\n'){ 73 | line[len-1] = 0; 74 | } 75 | int first = 0; 76 | printf("[ "); 77 | for(int i = 0; i < g->num_links; i++){ 78 | if(!strcmp(line, g->links[i].data)){ 79 | if(first){ 80 | printf(", "); 81 | } else { 82 | first = 1; 83 | } 84 | printf("[%d, %d]", g->links[i].source, g->links[i].target); 85 | } 86 | } 87 | printf(" ]\n"); 88 | free(line); 89 | } 90 | 91 | struct Tree *get_outgoing_ids(int source, struct Graph *g){ 92 | struct Tree *new_tree = (struct Tree *) malloc(sizeof(struct Tree)); 93 | new_tree->root = source; 94 | struct Array *outgoing = (struct Array *) malloc(sizeof(struct Array)); 95 | outgoing->data = (int *) malloc(sizeof(int)); 96 | outgoing->length = 0; 97 | new_tree->children = outgoing; 98 | int i = 0; 99 | while(i < g->num_links){ 100 | if(g->links[i].source == source){ 101 | outgoing->length++; 102 | outgoing->data = (int *) realloc(outgoing->data, sizeof(int) * outgoing->length); 103 | outgoing->data[outgoing->length-1] = g->links[i].target; 104 | } 105 | i++; 106 | } 107 | return new_tree; 108 | } 109 | 110 | struct Array *search(int node_id, int target, struct Array *path, struct Graph *g){ 111 | struct Tree *tree; 112 | struct Array *newPath = malloc(sizeof(struct Array)); 113 | newPath->length = 0; 114 | tree = get_outgoing_ids(node_id, g); 115 | for(int i = 0; i < tree->children->length; i++){ 116 | newPath->length = path->length+1; 117 | newPath->data = malloc(sizeof(int) * newPath->length); 118 | for(int l = 0; l < path->length; l++){ 119 | newPath->data[l] = path->data[l]; 120 | } 121 | newPath->data[newPath->length-1] = tree->children->data[i]; 122 | if(tree->children->data[i] == target){ 123 | free(tree->children); 124 | free(tree); 125 | return newPath; 126 | } else { 127 | newPath = search(tree->children->data[i], target, newPath, g); 128 | } 129 | } 130 | free(tree->children); 131 | free(tree); 132 | return newPath; 133 | } 134 | 135 | void test_for_path(char *name_a, char *name_b, struct Graph *g){ 136 | int start = get_id_by_name(name_a, g); 137 | int end = get_id_by_name(name_b, g); 138 | struct Array *path = malloc(sizeof(struct Array)); 139 | path->length = 1; 140 | path->data = malloc(sizeof(int)); 141 | path->data[0] = start; 142 | struct Array *endPath = search(start, end, path, g); 143 | if(endPath->length != 0 && endPath->data[endPath->length-1] == end){ 144 | printf("{ \"hasPath\": true, \"path\": [ "); 145 | for(int i = 0; i < endPath->length; i++){ 146 | if(i > 0){ 147 | printf(", "); 148 | } 149 | printf("%d", endPath->data[i]); 150 | } 151 | printf(" ] }\n"); 152 | } else { 153 | printf("{ \"hasPath\": false }\n"); 154 | } 155 | free(path->data); 156 | free(path); 157 | free(endPath->data); 158 | free(endPath); 159 | } 160 | -------------------------------------------------------------------------------- /link_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINK_UTILITY_H 2 | #define _LINK_UTILITY_H 3 | 4 | #include "structures.h" 5 | 6 | void write_link_data(char *line, char *name_a, char *name_b, struct Graph *g); 7 | void write_link_data_prompt(char *name_a, char *name_b, struct Graph *g); 8 | void read_link_data(char *name_a, char *name_b, struct Graph *g); 9 | void add_new_link(char *name_a, char *name_b, struct Graph *g); 10 | void get_links(struct Graph *g); 11 | struct Tree *get_outgoing_ids(int source, struct Graph *g); 12 | struct Array *search(int node_id, int target, struct Array *path, struct Graph *g); 13 | void test_for_path(char *name_a, char *name_b, struct Graph *g); 14 | 15 | #endif /* _LINK_UTILITY_H */ 16 | -------------------------------------------------------------------------------- /main_utility.c: -------------------------------------------------------------------------------- 1 | #include "main_utility.h" 2 | 3 | #include /* for fgetc(3), stdin, and EOF */ 4 | #include /* for malloc(3), realloc(3), and free(3) */ 5 | 6 | char *get_line() { 7 | char *line = malloc(300); 8 | int i = 0; 9 | while(i == 0 || line[i-1] != '\n'){ 10 | line[i] = getchar(); 11 | if(i > 299 && line[i] != '\n'){ 12 | printf("{ error: \"Input truncated, reached maximum length.\" }\n"); 13 | line[i] = '\n'; 14 | char c[1]; 15 | while((c[0] = getchar()) != '\n' && c[0] != EOF) 16 | /* discard */ ; 17 | break; 18 | } 19 | i++; 20 | } 21 | return line; 22 | } 23 | -------------------------------------------------------------------------------- /main_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAIN_UTILITY_H 2 | #define _MAIN_UTILITY_H 3 | 4 | char *get_line(); 5 | 6 | #endif /* _MAIN_UTILITY_H */ 7 | -------------------------------------------------------------------------------- /node_math.c: -------------------------------------------------------------------------------- 1 | #include "node_math.h" 2 | 3 | #include /* for printf(3) */ 4 | #include /* for strtod(3) */ 5 | 6 | #include "node_utility.h" /* for return_node_data() */ 7 | 8 | int add_nodes(char *name_a, char *name_b, struct Graph *g){ 9 | double double_val_a, double_val_b; 10 | double_val_a = strtod(return_node_data(name_a, g), NULL); 11 | double_val_b = strtod(return_node_data(name_b, g), NULL); 12 | double result = double_val_a + double_val_b; 13 | printf("%f\n", result); 14 | return result; 15 | } 16 | 17 | int divide_nodes(char *name_a, char *name_b, struct Graph *g){ 18 | double double_val_a, double_val_b; 19 | double_val_a = strtod(return_node_data(name_a, g), NULL); 20 | double_val_b = strtod(return_node_data(name_b, g), NULL); 21 | double result = double_val_a / double_val_b; 22 | printf("%f\n", result); 23 | return result; 24 | } 25 | 26 | int multiply_nodes(char *name_a, char *name_b, struct Graph *g){ 27 | double double_val_a, double_val_b; 28 | double_val_a = strtod(return_node_data(name_a, g), NULL); 29 | double_val_b = strtod(return_node_data(name_b, g), NULL); 30 | double result = double_val_a * double_val_b; 31 | printf("%f\n", result); 32 | return result; 33 | } 34 | 35 | int subtract_nodes(char *name_a, char *name_b, struct Graph *g){ 36 | double double_val_a, double_val_b; 37 | double_val_a = strtod(return_node_data(name_a, g), NULL); 38 | double_val_b = strtod(return_node_data(name_b, g), NULL); 39 | double result = double_val_a - double_val_b; 40 | printf("%f\n", result); 41 | return result; 42 | } 43 | -------------------------------------------------------------------------------- /node_math.h: -------------------------------------------------------------------------------- 1 | #ifndef _MATH_H 2 | #define _MATH_H 3 | 4 | #include "structures.h" 5 | 6 | int add_nodes(char *name_a, char *name_b, struct Graph *g); 7 | int divide_nodes(char *name_a, char *name_b, struct Graph *g); 8 | int multiply_nodes(char *name_a, char *name_b, struct Graph *g); 9 | int subtract_nodes(char *name_a, char *name_b, struct Graph *g); 10 | 11 | #endif /* _MATH_H */ 12 | -------------------------------------------------------------------------------- /node_utility.c: -------------------------------------------------------------------------------- 1 | #include "node_utility.h" 2 | 3 | #include /* for printf(3) */ 4 | #include /* for malloc(3) and free(3) */ 5 | #include /* for strcpy(3), strlen(3), and strcmp(3) */ 6 | 7 | #include "main_utility.h" /* for get_line() */ 8 | #include "input.h" /* for process_input() */ 9 | 10 | struct Node *get_node_by_id(unsigned int id, struct Graph *g){ 11 | for(int i = 0; i < g->num_nodes; i++){ 12 | if(g->nodes[i].id == id){ 13 | return &g->nodes[i]; 14 | } 15 | } 16 | struct Node *target = malloc(sizeof(struct Node)); 17 | target->name = malloc(5); 18 | strcpy(target->name, "NULL\0"); 19 | return target; 20 | } 21 | 22 | int node_exists(char *name, struct Graph *g){ 23 | for(int i = 0; i < g->num_nodes; i++){ 24 | if(!strcmp(g->nodes[i].name, name)){ 25 | return 1; 26 | } 27 | } 28 | return 0; 29 | } 30 | 31 | int add_new_node(char *name, struct Graph *g){ 32 | g->num_nodes++; 33 | int id = g->num_nodes-1; 34 | struct Node new_node; 35 | new_node.id = id; 36 | new_node.data = "NULL"; 37 | g->nodes[id] = new_node; 38 | g->nodes[id].name = malloc(strlen(name)+1); 39 | strcpy(g->nodes[id].name, name); 40 | return id; 41 | } 42 | 43 | int new_or_existing_id(char *name, struct Graph *g){ 44 | int id = -1; 45 | for(int i = 0; i < g->num_nodes; i++){ 46 | if(strcmp(g->nodes[i].name, name) == 0){ 47 | id = g->nodes[i].id; 48 | } 49 | } 50 | if(id == -1){ 51 | id = add_new_node(name, g); 52 | } 53 | return id; 54 | } 55 | 56 | int get_id_by_name(char *name, struct Graph *g){ 57 | int id = -1; 58 | for(int i = 0; i < g->num_nodes; i++){ 59 | if(strcmp(g->nodes[i].name, name) == 0){ 60 | id = g->nodes[i].id; 61 | } 62 | } 63 | return id; 64 | } 65 | 66 | char *return_node_data(char *name, struct Graph *g){ 67 | int i = 0; 68 | while(strcmp(name, g->nodes[i].name) != 0 && i <= g->num_nodes){ 69 | i++; 70 | } 71 | return g->nodes[i].data; 72 | } 73 | 74 | void print_outgoing_nodes(char *name, struct Graph *g){ 75 | int id = get_id_by_name(name, g); 76 | int first = 0; 77 | printf("[ "); 78 | struct Node node[1]; 79 | for(int i = 0; i < g->num_links; i++){ 80 | if(g->links[i].source == id){ 81 | if(first){ 82 | printf(", "); 83 | } else { 84 | first = 1; 85 | } 86 | node[0] = *get_node_by_id(g->links[i].target, g); 87 | printf("{ \"id\": %d, \"name\": \"%s\" }", node[0].id, node[0].name); 88 | } 89 | } 90 | printf(" ]\n"); 91 | } 92 | 93 | void print_incoming_nodes(char *name, struct Graph *g){ 94 | int id = get_id_by_name(name, g); 95 | int first = 0; 96 | printf("[ "); 97 | struct Node node[1]; 98 | for(int i = 0; i < g->num_links; i++){ 99 | if(g->links[i].target == id){ 100 | if(first){ 101 | printf(", "); 102 | } else { 103 | first = 1; 104 | } 105 | node[0] = *get_node_by_id(g->links[i].source, g); 106 | printf("{ \"id\": %d, \"name\": \"%s\" }", node[0].id, node[0].name); 107 | } 108 | } 109 | printf(" ]\n"); 110 | } 111 | 112 | void write_data(char *line, char *name, struct Graph *g){ 113 | int len = strlen(line); 114 | if(line[len-1] == '\n'){ 115 | line[len-1] = 0; 116 | } 117 | int i = 0; 118 | while(strcmp(name, g->nodes[i].name) != 0 && i <= g->num_nodes){ 119 | i++; 120 | } 121 | g->nodes[i].data = malloc(len); 122 | strcpy(g->nodes[i].data, line); 123 | } 124 | 125 | void write_data_prompt(char *name, struct Graph *g){ 126 | if(!node_exists(name, g)){ 127 | printf("{ error: \"No node with name %s.\" }\n", name); 128 | } else { 129 | char *line; 130 | printf("[(%s).data]> ", name); 131 | line = get_line(); 132 | write_data(line, name, g); 133 | free(line); 134 | } 135 | } 136 | 137 | void read_data(char *name, struct Graph *g){ 138 | if(!g->num_nodes){ 139 | printf("{ error: \"No nodes in graph.\" }\n"); 140 | return; 141 | } else { 142 | int i = 0; 143 | while(i < g->num_nodes && strcmp(name, g->nodes[i].name) != 0){ 144 | i++; 145 | } 146 | if(i == g->num_nodes){ 147 | printf("{ error: \"No node with name %s.\" }\n", name); 148 | } else { 149 | printf("{ id: %d, \"name\": \"%s\", \"data\": \"%s\" }\n", g->nodes[i].id, name, g->nodes[i].data); 150 | } 151 | } 152 | } 153 | 154 | void get_nodes(struct Graph *g){ 155 | char *line; 156 | printf("[node.data]> "); 157 | line = get_line(); 158 | int len = strlen(line); 159 | if(line[len-1] == '\n'){ 160 | line[len-1] = 0; 161 | } 162 | int first = 0; 163 | printf("[ "); 164 | for(int i = 0; i < g->num_nodes; i++){ 165 | if(!strcmp(line, g->nodes[i].data)){ 166 | if(first){ 167 | printf(", "); 168 | } else { 169 | first = 1; 170 | } 171 | printf("{ id: %d, \"name\": \"%s\", \"data\": \"%s\" }", g->nodes[i].id, g->nodes[i].name, g->nodes[i].data); 172 | } 173 | } 174 | printf(" ]\n"); 175 | free(line); 176 | } 177 | 178 | void run_command(char *name, struct Graph *g){ 179 | if(!g->num_nodes){ 180 | printf("{ error: \"No nodes in graph.\" }\n"); 181 | return; 182 | } else { 183 | int i = 0; 184 | while(i < g->num_nodes && strcmp(name, g->nodes[i].name) != 0){ 185 | i++; 186 | } 187 | if(i == g->num_nodes){ 188 | printf("{ error: \"No node with name %s.\" }\n", name); 189 | } else { 190 | process_input(g->nodes[i].data, g); 191 | } 192 | } 193 | } 194 | 195 | void print_graph(struct Graph *g){ 196 | if(g->num_nodes == 0 && g->num_links == 0){ 197 | printf("{ error: \"No data in graph.\" }\n"); 198 | } else { 199 | printf("{ "); 200 | int has_nodes = 0; 201 | if(g->num_nodes > 0){ 202 | has_nodes = 1; 203 | printf("\"nodes\": ["); 204 | int i = 0; 205 | while(i < g->num_nodes){ 206 | if(i > 0 && i <= g->num_nodes-1){ 207 | printf(", "); 208 | } 209 | printf("{ \"id\": %d, \"name\": \"%s\", \"data\": \"%s\" }", g->nodes[i].id, g->nodes[i].name, g->nodes[i].data); 210 | i++; 211 | } 212 | printf("]"); 213 | } 214 | if(g->num_links > 0){ 215 | if(has_nodes){ 216 | printf(", "); 217 | } 218 | printf("\"links\": ["); 219 | int l = 0; 220 | while(l < g->num_links){ 221 | if(l > 0 && l <= g->num_links-1){ 222 | printf(", "); 223 | } 224 | printf("{ \"source\": %d, \"target\": \"%d\", \"data\": \"%s\" }", g->links[l].source, g->links[l].target, g->links[l].data); 225 | l++; 226 | } 227 | printf("] }\n"); 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /node_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _NODE_UTILITY_H 2 | #define _NODE_UTILITY_H 3 | 4 | #include "structures.h" 5 | 6 | struct Node *get_node_by_id(unsigned int id, struct Graph *g); 7 | int add_new_node(char *name, struct Graph *g); 8 | int node_exists(char *name, struct Graph *g); 9 | int new_or_existing_id(char *name, struct Graph *g); 10 | int get_id_by_name(char *name, struct Graph *g); 11 | char *return_node_data(char *name, struct Graph *g); 12 | void print_outgoing_nodes(char *name, struct Graph *g); 13 | void print_incoming_nodes(char *name, struct Graph *g); 14 | void write_data(char *line, char *name, struct Graph *g); 15 | void write_data_prompt(char *name, struct Graph *g); 16 | void read_data(char *name, struct Graph *g); 17 | void get_nodes(struct Graph *g); 18 | void run_command(char *name, struct Graph *g); 19 | void print_graph(struct Graph *g); 20 | 21 | #endif /* _NODE_UTILITY_H */ 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Graph Reply 2 | =========== 3 | I have been teaching myself C for a while and Graph Reply is my first attempt at a REPL style program. It stores data in graph form and supports a variety of read/write operations including saving to disk. To run, compile the source code and run the output file. You will be taken to a repl where you can type commands. To close the program enter the command "exit". 4 | ```shell 5 | make 6 | ./reply 7 | > new mynode 8 | > save mygraph 9 | > exit 10 | ``` 11 | 12 | 13 | WRITE DATA 14 | ---------- 15 | Creates a node with label "person" 16 | ```code 17 | > new person 18 | ``` 19 | 20 | Creates a directed edge from person (source) to manager (target), the nodes are created if they do not exist 21 | ```code 22 | > link person manager 23 | ``` 24 | 25 | The command wn (write node) sets the data attribute of a given node. 26 | Here we set the data attribute of person to "John". 27 | ```code 28 | > wn person 29 | [(person).data]> John 30 | ``` 31 | 32 | The command wl (write link) sets the data attribute of the edge from person to manager, in this case it is given the value "has job". 33 | ```code 34 | > wl person manager 35 | [(person->manager).data]> has job 36 | ``` 37 | 38 | READ DATA 39 | --------- 40 | You can use the id or name of a node to get its data with the id and rn (read node) commands respectively. 41 | ```code 42 | > id 0 43 | { id: 0, "name": "person", "data": "John" } 44 | > rn person 45 | { id: 0, "name": "person", "data": "John" } 46 | ``` 47 | 48 | Use the rl (read link) command to get the edge information for two nodes 49 | ```code 50 | > rl person manager 51 | { "source": "person", "target": "manager", "data": "has job" } 52 | ``` 53 | 54 | The command out returns an array of nodes connected to the given node by outgoing edges. 55 | ```code 56 | > out person 57 | [ { "id": 1, "name": "manager" } ] 58 | ``` 59 | 60 | The command in returns an array of nodes connected to the given node by incoming edges. 61 | ```code 62 | > in manager 63 | [ { "id": 0, "name": "person" } ] 64 | ``` 65 | 66 | The command getn (get node) asks for data and then returns all nodes with the given data. 67 | ```code 68 | > getn 69 | [node.data]> John 70 | [ { id: 0, "name": "person", "data": "John" } ] 71 | ``` 72 | 73 | The command getl (get link) asks for data and then returns a pair of ids where the first id is the source and the second the target of an edge with the given data. 74 | ```code 75 | > getl 76 | [link.data]> has job 77 | [ [0, 1] ] 78 | ``` 79 | 80 | The command path takes two nodes as arguments and returns the first path it finds through the graph via outgoing edges from the first node to the second, or returns false if no path exists. 81 | ```code 82 | > link fish bird 83 | > link bird cat 84 | > path fish cat 85 | { "hasPath": true, "path": [ 0, 1, 2 ] } 86 | ``` 87 | 88 | The command all will print every node and edge in the graph in one fell swoop. 89 | ```code 90 | > all 91 | { "nodes": [{ "id": 0, "name": "person", "data": "John" }, { "id": 1, "name": "manager", "data": "NULL" }], "links": [{ "source": 0, "target": "1", "data": "has job" }] } 92 | ``` 93 | 94 | Math 95 | ---- 96 | Basic arithmetic operations are possible if numbers are stored on the data attribute of nodes, the following examples will suffice to illustrate their usage: 97 | ```code 98 | > link a b 99 | > wn a 100 | [(a).data]> 10 101 | > wn b 102 | [(b).data]> 2.5 103 | > add a b 104 | 12.500000 105 | > sub a b 106 | 7.500000 107 | > mult a b 108 | 25.000000 109 | > div a b 110 | 4.000000 111 | ``` 112 | Aliasing Commands 113 | ---------------- 114 | Commands can be stored on the data property of a node and run using the cmd command. 115 | ```code 116 | > link a b 117 | > wn a 118 | [(a).data]> 10 119 | > wn b 120 | [(b).data]> 2.5 121 | > new operation 122 | > wn operation 123 | [(operation).data]> add a b 124 | > cmd operation 125 | 12.50000 126 | ``` 127 | Disk Storage 128 | ---- 129 | ```code 130 | > save my_graph 131 | ``` 132 | The save command converts the state of the graph into graph-reply commands and saves them to a file with the name given as input. The above command saves the graph in the file my_graph.gph. 133 | ```code 134 | > load my_graph 135 | ``` 136 | The load command loads a previously saved graph into memory from a file. 137 | -------------------------------------------------------------------------------- /reply.c: -------------------------------------------------------------------------------- 1 | #include /* for printf(3) */ 2 | #include /* for free(3) */ 3 | #include /* for strcmp(3) and strcpy(3) */ 4 | 5 | #include "input.h" /* for process_input() */ 6 | #include "main_utility.h" /* for get_line() */ 7 | 8 | int main (){ 9 | char *line, test[6]; 10 | strcpy(test, "exit\n"); 11 | struct Graph G; 12 | G.num_nodes = 0; 13 | G.num_links = 0; 14 | while(1) { 15 | printf("> "); 16 | line = get_line(); 17 | if(strcmp(line, test) == 0) { 18 | free(line); 19 | break; 20 | } 21 | process_input(line, &G); 22 | free(line); 23 | } 24 | int i = 0; 25 | while(i < G.num_nodes){ 26 | free(G.nodes[i].name); 27 | i++; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /structures.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRUCTURES_H 2 | #define _STRUCTURES_H 3 | 4 | struct Node { 5 | char *name; 6 | char *data; 7 | unsigned int id; 8 | }; 9 | 10 | struct Link { 11 | int source; 12 | int target; 13 | char *data; 14 | }; 15 | 16 | struct Graph { 17 | struct Node nodes[10000]; 18 | struct Link links[10000]; 19 | int num_nodes; 20 | int num_links; 21 | }; 22 | 23 | struct Array { 24 | int length; 25 | int *data; 26 | }; 27 | 28 | struct Tree { 29 | int root; 30 | struct Array *children; 31 | }; 32 | 33 | struct Forest { 34 | int levels; 35 | struct Tree *trees; 36 | }; 37 | 38 | #endif /* _STRUCTURES_H */ 39 | -------------------------------------------------------------------------------- /tree_utility.c: -------------------------------------------------------------------------------- 1 | #include "tree_utility.h" 2 | 3 | int tree_contains(int target, struct Tree *tree){ 4 | int contains = 0; 5 | int i = 0; 6 | while(i < tree->children->length){ 7 | if(tree->children->data[i] == target){ 8 | contains = 1; 9 | break; 10 | } 11 | i++; 12 | } 13 | return contains; 14 | } 15 | -------------------------------------------------------------------------------- /tree_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _TREE_UTILITY_H 2 | #define _TREE_UTILITY_H 3 | 4 | #include "structures.h" 5 | 6 | int tree_contains(int target, struct Tree *tree); 7 | 8 | #endif /* _TREE_UTILITY_H */ 9 | --------------------------------------------------------------------------------