aaaaaa123456789 icon

TPP song list parser

aaaaaa123456789 | PRO | 11/18/15 11:28:19 AM UTC | 0 ⭐ | 441 👁️ | Never ⏰ | []
C |

7.61 KB

|

None

|

0 👍

/

0 👎

#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