#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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);
}
Comments