aaaaaa123456789 icon

Forum metrics collector

aaaaaa123456789 | PRO | 08/31/15 02:56:54 PM UTC | 0 ⭐ | 423 👁️ | Never ⏰ | []
C |

10.21 KB

|

None

|

0 👍

/

0 👎

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
 
struct chunk {
  char type;
  char * contents;
};
 
int main(void);
struct chunk * get_input_chunks(void);
char * get_line(void);
void destroy_input_chunks(struct chunk *);
void parse_chunk(const struct chunk *);
unsigned execute_query(const char *, char ****, unsigned *);
char * next_output_line(char **);
void destroy_query_results(char ***);
unsigned execute_program(const char *, char **, const void *, unsigned, void **);
void execute_subprocess(const char *, char **, int, int);
void display_percentage_table(const char *);
char * convert_number_to_string(unsigned long long);
char * convert_percentage_to_string(unsigned short);
 
char * database = NULL;
char last_number[40];
char last_percentage[8];
 
int main (void) {
  struct chunk * chunks = get_input_chunks();
  if (!chunks) return 0;
  unsigned chunkID;
  for (chunkID = 0; chunks[chunkID].type; chunkID ++) parse_chunk(chunks + chunkID);
  destroy_input_chunks(chunks);
  return 0;
}
 
struct chunk * get_input_chunks (void) {
  struct chunk * chunks = NULL;
  unsigned chunk_count = 0;
  char * line = get_line();
  struct chunk current_chunk = {.type = 0, .contents = NULL};
  unsigned c4 = 0; // current chunk contents count
  unsigned line_length;
  while (line) {
    line_length = strlen(line);
    if (*line == current_chunk.type) {
      if (!current_chunk.type) {
        line = get_line();
        continue;
      }
      *line = '\n';
      if (c4) {
        current_chunk.contents = realloc(current_chunk.contents, c4 + line_length);
        memcpy(current_chunk.contents + c4, line, line_length);
        c4 += line_length;
      } else {
        current_chunk.contents = malloc(c4 = line_length - 1);
        memcpy(current_chunk.contents, line + 1, c4);
      }
      free(line);
      line = get_line();
      continue;
    }
    if (current_chunk.type) {
      c4[current_chunk.contents = realloc(current_chunk.contents, c4 + 1)] = 0;
      chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1));
      chunks[chunk_count ++] = current_chunk;
    }
    current_chunk.type = *line;
    current_chunk.contents = NULL;
    c4 = 0;
  }
  if (current_chunk.type) {
    chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1));
    chunks[chunk_count ++] = current_chunk;
  }
  chunk_count[chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1))] = (struct chunk) {.type = 0, .contents = NULL};
  return chunks;
}
 
char * get_line (void) {
  char * line = NULL;
  unsigned length = 0;
  int next_char = getchar();
  if (next_char == EOF) return NULL;
  while ((next_char != EOF) && (next_char != '\n')) {
    line = realloc(line, length + 1);
    line[length ++] = next_char;
    next_char = getchar();
  }
  line = realloc(line, length + 1);
  line[length] = 0;
  return line;
}
 
void destroy_input_chunks (struct chunk * chunks) {
  struct chunk * chunk;
  for (chunk = chunks; chunk -> type; chunk ++) free(chunk -> contents);
  free(chunks);
}
 
void parse_chunk (const struct chunk * chunk) {
  if (!chunk) return;
  switch (chunk -> type) {
    case ' ':
      printf("%s\n", chunk -> contents);
      return;
    case '%':
      display_percentage_table(chunk -> contents);
      return;
    case '!':
      if (database) free(database);
      database = strcpy(malloc(strlen(chunk -> contents) + 1), chunk -> contents);
      return;
    case 0:
      return;
    default:
      fprintf(stderr, "unknown chunk type: %c\n", chunk -> type);
  }
}
 
unsigned execute_query (const char * query, char **** results, unsigned * column_count) {
  if (!(query && database)) goto argument_error;
  char * arguments[4];
  *arguments = "mysql";
  arguments[1] = "-sr";
  arguments[2] = database;
  arguments[3] = NULL;
  char * output;
  unsigned output_size = execute_program(*arguments, arguments, query, strlen(query), (void **) &output);
  if (!(output && output_size)) goto output_error;
  output = realloc(output, 1 + output_size);
  output[output_size] = 0;
  char * buffer = output;
  char * line = next_output_line(&buffer);
  char * first_line = line;
  if (!(line && *line)) goto output_error;
  unsigned col_count = 1;
  while (first_line = strchr(first_line, '\t')) first_line ++, col_count ++;
  if (column_count) *column_count = col_count;
  unsigned row_count = 0;
  if (!results) {
    while (buffer = strchr(buffer, '\n')) buffer ++, row_count ++;
    free(output);
    return row_count;
  }
  *results = NULL;
  char *** row;
  unsigned col;
  unsigned len;
  char * tabpos;
  while (line) {
    row = (*results = realloc(*results, sizeof(char **) * (1 + row_count))) + row_count;
    row_count ++;
    *row = calloc(col_count + 1, sizeof(char *));
    for (col = 0; col < col_count; col ++) {
      if (!line) break;
      len = (tabpos = strchr(line, '\t')) ? tabpos - line : strlen(line);
      memcpy(col[*row] = malloc(len + 1), line, len);
      (*row)[col][len] = 0;
      line = tabpos;
      if (tabpos) line ++;
    }
    line = next_output_line(&buffer);
  }
  row_count[*results = realloc(*results, sizeof(char **) * (1 + row_count))] = NULL;
  free(output);
  return row_count;
  output_error:
  free(output);
  argument_error:
  if (results) *results = NULL;
  if (column_count) *column_count = 0;
  return 0;
}
 
char * next_output_line (char ** string) {
  if (!(string && *string && **string)) return NULL;
  unsigned line_length = strcspn(*string, "\r\n");
  char * result = *string;
  *string += line_length + 1 + !strncmp(*string + line_length, "\r\n", 2);
  result[line_length] = 0;
  return result;
}
 
void destroy_query_results (char *** results) {
  char *** row;
  char ** cell;
  for (row = results; *row; row ++) {
    for (cell = *row; *cell; cell ++) free(*cell);
    free(*row);
  }
  free(results);
}
 
unsigned execute_program (const char * program, char ** arguments, const void * input, unsigned input_size, void ** output) {
  int file_descriptors[4];
  pipe(file_descriptors);
  pipe(file_descriptors + 2);
  fcntl(file_descriptors[1], F_SETFD, FD_CLOEXEC);
  fcntl(file_descriptors[2], F_SETFD, FD_CLOEXEC);
  int pid = fork();
  if (!pid) execute_subprocess(program, arguments, *file_descriptors, file_descriptors[3]);
  close(*file_descriptors);
  close(file_descriptors[3]);
  if (input) write(file_descriptors[1], input, input_size);
  close(file_descriptors[1]);
  char buffer[4096];
  unsigned output_size = 0;
  int reception;
  if (output) *output = NULL;
  while (reception = read(file_descriptors[2], buffer, 4096)) {
    if (reception < 0) {
      if ((errno == EAGAIN) || (errno == EINTR)) continue;
      break;
    }
    if (output) {
      *output = realloc(*output, output_size + reception);
      memcpy(((char *) *output) + output_size, buffer, reception);
    }
    output_size += reception;
  }
  close(file_descriptors[2]);
  waitpid(pid, NULL, 0);
  return output_size;
}
 
void execute_subprocess (const char * program, char ** arguments, int read_fd, int write_fd) {
  // must not return to caller
  while (dup2(read_fd, 0) < 0);
  close(read_fd);
  while (dup2(write_fd, 1) < 0);
  close(write_fd);
  execvp(program, arguments);
  abort();
}
 
#define PERCENTAGE_TABLE_ROW "[tr][td=width: 270px; border-bottom: 1px solid #ccc; padding-left: 6px]%s[/td]\
                              [td=width: 80px; text-align: right; border-bottom: 1px solid #ccc]%s[/td]\
                              [td=width: 50px; text-align: right; border-bottom: 1px solid #ccc; padding-right: 6px]%s[/td][/tr]"
 
#define PERCENTAGE_TABLE_TOTAL_ROW "[tr][td=width: 270px; text-align: right; font-weight: bold]Total[/td]\
                                    [td=width: 80px; text-align: right]%s[/td]\
                                    [td=width: 50px; text-align: right; padding-right: 6px; font-weight: bold]100[/td][/tr]"
 
void display_percentage_table (const char * query) {
  char *** results;
  unsigned column_count;
  unsigned row_count = execute_query(query, &results, &column_count);
  if (column_count != 2) fputs("warning: percentage query did not return two columns\n", stderr);
  if (!row_count) {
    destroy_query_results(results);
    return;
  }
  unsigned long long * item_counts = malloc(row_count * sizeof(unsigned long long));
  unsigned row;
  char * t;
  unsigned long long total = 0;
  for (row = 0; row < row_count; row ++) {
    item_counts[row] = strtoull(results[row][1], &t, 10);
    if (*t) fprintf(stderr, "warning: illegal numeric value at row %u: %s\n", row + 1, results[row][1]);
    total += item_counts[row];
  }
  unsigned short * percentages = malloc(row_count * sizeof(unsigned short));
  unsigned short total_percentages = 0;
  for (row = 0; row < row_count; row ++) {
    percentages[row] = (item_counts[row] * 20000 / total + 1) >> 1;
    total_percentages += percentages[row];
  }
  unsigned short max = *percentages;
  unsigned max_row = 0;
  for (row = 1; row < row_count; row ++) if (percentages[row] > max) {
    max_row = row;
    max = percentages[row];
  }
  percentages[max_row] = percentages[max_row] + 10000 - total_percentages;
  puts("[table]");
  for (row = 0; row < row_count; row ++)
    printf(PERCENTAGE_TABLE_ROW, results[row][0], convert_number_to_string(item_counts[row]), convert_percentage_to_string(percentages[row]));
  printf(PERCENTAGE_TABLE_TOTAL_ROW, convert_number_to_string(total));
  puts("[/table]\n");
  destroy_query_results(results);
  free(item_counts);
  free(percentages);
}
 
char * convert_number_to_string (unsigned long long number) {
  if (number < 1000) {
    sprintf(last_number, "%llu", number);
    return last_number;
  }
  convert_number_to_string(number / 1000);
  char remainder[5];
  sprintf(remainder, ",%03llu", number % 1000);
  strcat(last_number, remainder);
  return last_number;
}
 
char * convert_percentage_to_string (unsigned short percentage) {
  if (percentage > 9999) {
    strcpy(last_percentage, "100");
    return last_percentage;
  }
  sprintf(last_percentage, "%2hu.%02hu", percentage / 100, percentage % 100);
  return last_percentage;
}

Comments