#include #include #include #include struct parsed_line { char * field_name; char * contents; int level; int start; }; struct item { int level; int count; char ** fields; char ** values; }; char * get_line(int *); struct parsed_line get_next_line(int *); void destroy_parsed_line(struct parsed_line); int valid_line(const char *); void trim(char *); int update_current_line(void); struct item get_next_item(int *); void destroy_item(struct item); const char * find_item_field(struct item, const char *); void parse_list(void); void output_game(const char *, int); void output_song(const char *, int); void close_output(int); void print_escaped_string(const char *); struct parsed_line current_line = {NULL, NULL, -1, -1}; int main (void) { int reachedEOF; char * line; while (1) { line = get_line(&reachedEOF); trim(line); if (!strcmp(line, "---")) break; free(line); if (reachedEOF) return 1; } free(line); struct parsed_line pl; while (1) { pl = get_next_line(&reachedEOF); if (!(pl.level || strcmp(pl.field_name, "games"))) break; destroy_parsed_line(pl); if (reachedEOF) return 1; } parse_list(); return 0; } char * get_line (int * reachedEOF) { char * result = NULL; unsigned length = 0; int input; while (1) { input = getchar(); if ((input == EOF) || (input == '\n')) { if (reachedEOF) *reachedEOF = input == EOF; break; } result = realloc(result, length + 1); result[length ++] = input; } if (result && (result[length - 1] == '\r')) result[length - 1] = 0; else { result = realloc(result, length + 1); result[length] = 0; } return result; } struct parsed_line get_next_line (int * reachedEOF) { char * line = NULL; int end; do { free(line); line = get_line(&end); } while (!(end || valid_line(line))); if (reachedEOF) *reachedEOF = end; if (end && !valid_line(line)) { free(line); return (struct parsed_line) {.field_name = NULL, .contents = NULL, .start = -1, .level = -1}; } struct parsed_line result; end = strspn(line, " "); if (!strncmp(line + end, "- ", 2)) { end += 2; result.start = 1; } else result.start = 0; result.level = end >> 2; char * real_line = line + end; end = strchr(real_line, ':') - real_line; result.field_name = malloc(end + 1); memcpy(result.field_name, real_line, end); result.field_name[end] = 0; trim(result.field_name); real_line += end + 1; if (*real_line) { char * quote_begin = strchr(real_line, '"'); char * quote_end = NULL; if (quote_begin) { quote_end = strchr(quote_begin + 1, '"'); while (quote_end && quote_end[-1] == '\\') quote_end = strchr(quote_end + 1, '"'); } if (quote_end) { result.contents = malloc(quote_end - quote_begin); memcpy(result.contents, quote_begin + 1, quote_end - quote_begin - 1); result.contents[quote_end - quote_begin - 1] = 0; } else { result.contents = strcpy(malloc(strlen(real_line) + 1), real_line); trim(result.contents); } } else result.contents = strcpy(malloc(1), ""); free(line); return result; } void destroy_parsed_line (struct parsed_line parsed_line) { free(parsed_line.field_name); free(parsed_line.contents); } int valid_line (const char * line) { if (*line == '#') return 0; if (!strchr(line, ':')) return 0; return 1; } void trim (char * string) { unsigned f; for (f = 0; string[f] && isspace(string[f]); f ++); unsigned lim = strlen(string) - f; memmove(string, string + f, lim + 1); if (!*string) return; while (isspace(string[lim - 1])) lim --; string[lim] = 0; } int update_current_line (void) { int reachedEOF; if (current_line.field_name) return 0; current_line = get_next_line(&reachedEOF); return reachedEOF; } int update_next_line (void) { if (current_line.field_name) destroy_parsed_line(current_line); current_line = (struct parsed_line) {.contents = NULL, .field_name = NULL, .level = -1, .start = -1}; return update_current_line(); } struct item get_next_item (int * reachedEOF) { int end = update_current_line(); if (!(current_line.field_name)) { if (reachedEOF) *reachedEOF = 1; return (struct item) {.level = -1, .count = -1, .fields = NULL, .values = NULL}; } while (!(current_line.start)) { end = update_next_line(); if (end && !(current_line.field_name)) { if (reachedEOF) *reachedEOF = 1; return (struct item) {.level = -1, .count = -1, .fields = NULL, .values = NULL}; } } struct item result = {.level = current_line.level, .count = 1, .fields = malloc(sizeof(char *)), .values = malloc(sizeof(char *))}; *(result.fields) = strcpy(malloc(strlen(current_line.field_name) + 1), current_line.field_name); *(result.values) = strcpy(malloc(strlen(current_line.contents) + 1), current_line.contents); while (1) { end = update_next_line(); if (!(current_line.field_name)) break; if (current_line.start) break; result.fields = realloc(result.fields, sizeof(char *) * (result.count + 1)); result.values = realloc(result.values, sizeof(char *) * (result.count + 1)); result.fields[result.count] = strcpy(malloc(strlen(current_line.field_name) + 1), current_line.field_name); result.values[result.count] = strcpy(malloc(strlen(current_line.contents) + 1), current_line.contents); result.count ++; } if (reachedEOF) *reachedEOF = end; return result; } void destroy_item (struct item item) { if (item.count < 0) return; unsigned p; for (p = 0; p < item.count; p ++) { free(item.fields[p]); free(item.values[p]); } free(item.fields); free(item.values); } const char * find_item_field (struct item item, const char * field) { if (item.count < 0) return NULL; unsigned p; for (p = 0; p < item.count; p ++) if (!strcmp(field, item.fields[p])) return item.values[p]; return NULL; } void parse_list (void) { struct item current_item = get_next_item(NULL); putchar('['); int last_level = 0; while (current_item.level > 0) { switch (current_item.level) { case 1: output_game(find_item_field(current_item, "title"), last_level); break; case 2: output_song(find_item_field(current_item, "title"), last_level); break; } last_level = current_item.level; destroy_item(current_item); current_item = get_next_item(NULL); } close_output(last_level); putchar(']'); } void output_game (const char * game, int last_level) { switch (last_level) { case 1: case 2: printf("%s", "]},"); case 0: printf("%s", "{\"game\":"); print_escaped_string(game); printf("%s", ",\"songs\":["); } } void output_song (const char * song, int last_level) { switch (last_level) { case 2: putchar(','); case 1: printf("\"%s\"", song); } } void close_output (int last_level) { switch (last_level) { case 1: case 2: printf("%s", "]}"); } } void print_escaped_string (const char * str) { char * string = strcpy(malloc(strlen(str) + 1), str); char * s = string; putchar('"'); char c; unsigned len; while (*string) { if (strchr("\"/\\", *string)) { printf("\\%c", *string); string ++; continue; } len = strcspn(string, "\"/\\"); c = string[len]; string[len] = 0; printf("%s", string); string[len] = c; string += len; } putchar('"'); free(s); }