changes in popup menu is now preserved
[taskasaur.git] / parser.c
1
2 #include "headers/parser.h"
3
4 typedef struct State {
5     Board* board;
6     TodoList* cur_todolist;
7     TodoItem* cur_todoitem;
8     /* for use during processing */
9     char* last_block_text;
10 } State;
11
12 char* read_file(char* file_name, long* size);
13
14 /* processing */
15 void enter_todolist(State* state, char* list_name);
16 void exit_todolist(State* state);
17
18 void enter_todoitem(State* state, char* item_name);
19 void exit_todoitem(State* state);
20
21 void set_description(State* state, char* description);
22 void set_due(State* state, char* due); // make an acc date struct later
23 void add_subtask(State* state, char* subtask_name, SubTaskState subtask_state);
24
25 /* callbacks to parser */
26 int enter_block(MD_BLOCKTYPE type, void* detail, void* userdata);
27 int leave_block(MD_BLOCKTYPE type, void* detail, void* userdata);
28 int enter_span(MD_SPANTYPE type, void* detail, void* userdata);
29 int leave_span(MD_SPANTYPE type, void* detail, void* userdata);
30 int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata);
31 void debug_log(const char* msg, void* userdata);
32 void syntax(void);
33
34 const MD_PARSER parser = {
35     0,
36     MD_FLAG_TASKLISTS,
37     &enter_block,
38     &leave_block,
39     &enter_span,
40     &leave_span,
41     &text,
42     &debug_log,
43     &syntax
44 };
45
46
47 /* reading */
48 Board*
49 begin_parse(char* board_path)
50 {
51     const char* input_buffer;
52     long input_size;
53     State state;
54     Board* new_board;
55
56     /* read entire file */
57     input_buffer = read_file(board_path, &input_size);
58
59     /* setup state */
60     state.cur_todolist = NULL;
61     state.cur_todoitem = NULL;
62
63     new_board = malloc(sizeof(Board));
64     new_board->todolist_list = malloc(0);
65     new_board->todolist_count = 0;
66     state.board = new_board;
67
68     md_parse(input_buffer, input_size, &parser, &state);
69
70     /* finish calls */
71     exit_todolist(&state);
72
73     free((char*)input_buffer);
74
75     return state.board;
76 }
77
78 char*
79 read_file(char* file_name, long* size) 
80 { // apparently using seek isnt the greatest, may change to chunk reading later
81     FILE* file;
82     char* output;
83     long fsize;
84
85     file = fopen(file_name, "r");
86     if (file == NULL) {
87         perror("read_file > fopen");
88         return NULL;
89     }
90
91     fseek(file, 0, SEEK_END);
92     fsize = ftell(file);
93     fseek(file, 0, SEEK_SET);
94
95     output = malloc(fsize+1); // maybe error check malloc
96     fread(output, 1, fsize, file);
97     if (ferror(file)) {
98         printf("Something went wrong reading file\n");
99         fclose(file);
100         free(output);
101         return NULL;
102     }
103     output[fsize] = 0; // add null terminator
104     
105     /* clean up */
106     fclose(file);
107
108     /* return */
109     *size = fsize;
110     return output;
111 }
112
113 void
114 enter_todolist(State* state, char* list_name)
115 {
116     TodoList* new_todolist;
117     
118     new_todolist = malloc(sizeof(TodoList));
119     new_todolist->list_name = list_name;
120     new_todolist->item_list = malloc(0);
121     new_todolist->item_count = 0;
122
123     state->cur_todolist = new_todolist;
124 }
125
126 void
127 exit_todolist(State* state)
128 {
129     #define sb state->board
130
131     if (state->cur_todolist == NULL) return; 
132
133     exit_todoitem(state);
134
135     sb->todolist_count += 1;
136     sb->todolist_list = realloc(sb->todolist_list, sb->todolist_count*sizeof(TodoList*));
137     sb->todolist_list[sb->todolist_count-1] = state->cur_todolist;
138     state->cur_todolist = NULL;
139
140     #undef sb
141 }
142
143 void
144 enter_todoitem(State* state, char* item_name)
145 {
146     TodoItem* new_todoitem;
147
148     new_todoitem = malloc(sizeof(TodoItem));
149     new_todoitem->item_name = item_name;
150     new_todoitem->description = strdup("");
151     new_todoitem->due = strdup("");
152     new_todoitem->subtask_list = malloc(0);
153     new_todoitem->subtask_count = 0;
154
155     state->cur_todoitem = new_todoitem;
156     
157 }
158
159 void
160 exit_todoitem(State* state)
161 {
162     #define st state->cur_todolist
163
164     if (state->cur_todoitem == NULL) return; 
165     
166     /* append current item to todo list */
167     st->item_count += 1;
168     st->item_list = realloc(st->item_list, st->item_count*sizeof(TodoItem*));
169     st->item_list[st->item_count-1] = state->cur_todoitem;
170     state->cur_todoitem = NULL;
171
172     #undef st
173 }
174
175 void
176 set_description(State* state, char* description)
177 {
178     state->cur_todoitem->description = description;
179 }
180
181 void
182 set_due(State* state, char* due)
183 {
184     state->cur_todoitem->due = due;
185 }
186
187 void
188 add_subtask(State* state, char* subtask_name, SubTaskState subtask_state)
189 {
190     SubTask* new_subtask;
191     SubTask** subtask_list;
192     
193     /* create new subtask */
194     new_subtask = malloc(sizeof(SubTask));
195     new_subtask->subtask_name = subtask_name;
196     new_subtask->done = subtask_state;
197
198     /* add it */
199     subtask_list = state->cur_todoitem->subtask_list;
200
201     state->cur_todoitem->subtask_count += 1;
202     subtask_list = realloc(subtask_list, state->cur_todoitem->subtask_count*sizeof(SubTask*));
203     subtask_list[state->cur_todoitem->subtask_count-1] = new_subtask;
204
205     state->cur_todoitem->subtask_list = subtask_list;
206
207 }
208
209 int
210 enter_block(MD_BLOCKTYPE type, void* detail, void* userdata)
211 {
212     return 0;
213 }
214
215 int
216 leave_block(MD_BLOCKTYPE type, void* detail, void* userdata)
217 {
218     State* state;
219     state = (State*)userdata;
220
221     switch (type) {
222
223         case MD_BLOCK_H:;
224
225             MD_BLOCK_H_DETAIL* h_detail;
226             h_detail = ((MD_BLOCK_H_DETAIL*)detail);
227
228             switch(h_detail->level) {
229
230                 case 1:
231                     break;
232
233                 case 2:
234                     exit_todolist(state);
235                     enter_todolist(state, state->last_block_text);
236                     break;
237
238                 case 3:
239                     exit_todoitem(state);
240                     enter_todoitem(state, state->last_block_text);
241                     break;
242             }
243
244             break;
245
246         case MD_BLOCK_QUOTE:
247             set_description(state, state->last_block_text);
248             break;
249
250         case MD_BLOCK_LI:;
251             MD_BLOCK_LI_DETAIL* li_detail;
252             SubTaskState done;
253
254             li_detail = ((MD_BLOCK_LI_DETAIL*)detail);
255
256             assert(li_detail->is_task != 0); // all lists should be task lists
257
258             done = li_detail->task_mark == ' ' ? SubTaskState_todo : SubTaskState_done;
259
260             add_subtask(state, state->last_block_text, done);
261             break;
262
263         // no need for default case for now :>
264     }
265     return 0;
266 }
267
268 int
269 enter_span(MD_SPANTYPE type, void* detail, void* userdata)
270 {
271
272
273     return 0;
274 }
275
276 int
277 leave_span(MD_SPANTYPE type, void* detail, void* userdata)
278 {
279     State* state;
280     state = (State*)userdata;
281     
282     switch (type) {
283         case MD_SPAN_STRONG:
284             set_due(state, state->last_block_text);
285             break;
286     }
287     return 0;
288 }
289
290 int
291 text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata){
292
293     State* state;
294     char* content;
295
296     state = (State*)userdata;
297
298     content = malloc(size*sizeof(char)+1);
299     memcpy(content, text, size);
300     content[size] = 0;
301
302     /* printf("%s\n", content); */
303     state->last_block_text = content;
304     
305     return 0;
306 }
307
308 void
309 debug_log(const char* msg, void* userdata)
310 {
311     printf(msg);
312     return;
313 }
314
315 void 
316 syntax(void)
317 {
318     return;
319 }
320
321 /* writing */
322 int
323 begin_write(char* board_path, Board* board)
324 { // TODO, make a backup file of board before write in case it crashes
325
326     FILE* file;
327
328     file = fopen(board_path, "w+");
329     if (file == NULL) {
330         perror("begin_write > fopen");
331         return -1;
332     }
333
334     for (int i = 0; i < board->todolist_count; i++) {
335         TodoList* cur_todolist = board->todolist_list[i];
336
337         fprintf(file, "## %s\n", cur_todolist->list_name);
338
339         for (int j = 0; j < cur_todolist->item_count; j++) {
340             TodoItem* cur_todoitem = cur_todolist->item_list[j];
341
342             fprintf(file, "### %s\n", cur_todoitem->item_name);
343             
344             /* bug rn, for some reason date is being written as description too */
345             if (strlen(cur_todoitem->description) > 0) {
346                 fprintf(file, "> %s\n", cur_todoitem->description);
347             }
348             if (strlen(cur_todoitem->due) > 0) {
349                 fprintf(file, "**%s**\n", cur_todoitem->due);
350             }
351
352             for (int k = 0; k < cur_todoitem->subtask_count; k++) {
353                 SubTask* cursubtask = cur_todoitem->subtask_list[k];
354
355                 char done_char = (cursubtask->done == SubTaskState_done) ? 'X' : ' ';
356                 fprintf(file, "- [%c] %s\n", done_char, cursubtask->subtask_name);
357
358             }
359
360         }
361
362     }
363
364     fclose(file);
365
366     return 0;
367 }
368
369 /* others */
370 void
371 log_todo(Board* board)
372 {
373     for (int i = 0; i < board->todolist_count; i++) {
374         TodoList* todolist;
375         printf("List =-=-=-=-=-==-=-=-=-=-=-\n");
376         todolist = board->todolist_list[i];
377         printf("List name: %s\n", todolist->list_name);
378         printf("Num of items: %d\n", todolist->item_count);
379
380         for (int j = 0; j < todolist->item_count; j++) {
381             TodoItem* todoitem;
382             printf("Item =-=-=-=-=-\n");
383             todoitem = todolist->item_list[j];
384             printf("Item name: %s\n", todoitem->item_name);
385             printf("Description: %s\n", todoitem->description);
386             printf("Num of subtasks: %d\n", todoitem->subtask_count);
387
388             for (int k = 0; k < todoitem->subtask_count; k++) {
389                 SubTask* subtask;
390                 int done;
391
392                 subtask = todoitem->subtask_list[k];
393                 printf("Subtask: %s, %d\n", subtask->subtask_name, subtask->done);
394             }
395         }
396     }
397 }
398
399 int
400 free_board(Board* board)
401 { // all the fields are being leaked rn
402
403     for (int i = 0; i < board->todolist_count; i++) {
404         TodoList* cur_todolist = board->todolist_list[i];
405
406         /* free(cur_todolist->list_name); */
407
408         for (int j = 0; j < cur_todolist->item_count; j++) {
409             TodoItem* cur_todoitem = cur_todolist->item_list[j];
410
411             /* free(cur_todoitem->item_name); */
412             /* free(cur_todoitem->description); */
413             /* free(cur_todoitem->due); */
414
415             /* free subtask later too */
416             for (int k = 0; k < cur_todoitem->subtask_count; k++) {
417                 SubTask* cur_subtask = cur_todoitem->subtask_list[k];
418
419                 /* free(cur_subtask->subtask_name); */
420                 free(cur_subtask);
421                 
422             }
423
424             free(cur_todoitem);
425         }
426
427         free(cur_todolist);
428     }
429
430     /* free(board->board_name); */
431     free(board);
432
433     return 0;
434 }