Advertisement
aaaaaa123456789

Forum metrics collector

Aug 31st, 2015
116
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.21 KB
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <errno.h>
  7. #include <sys/wait.h>
  8.  
  9. struct chunk {
  10.   char type;
  11.   char * contents;
  12. };
  13.  
  14. int main(void);
  15. struct chunk * get_input_chunks(void);
  16. char * get_line(void);
  17. void destroy_input_chunks(struct chunk *);
  18. void parse_chunk(const struct chunk *);
  19. unsigned execute_query(const char *, char ****, unsigned *);
  20. char * next_output_line(char **);
  21. void destroy_query_results(char ***);
  22. unsigned execute_program(const char *, char **, const void *, unsigned, void **);
  23. void execute_subprocess(const char *, char **, int, int);
  24. void display_percentage_table(const char *);
  25. char * convert_number_to_string(unsigned long long);
  26. char * convert_percentage_to_string(unsigned short);
  27.  
  28. char * database = NULL;
  29. char last_number[40];
  30. char last_percentage[8];
  31.  
  32. int main (void) {
  33.   struct chunk * chunks = get_input_chunks();
  34.   if (!chunks) return 0;
  35.   unsigned chunkID;
  36.   for (chunkID = 0; chunks[chunkID].type; chunkID ++) parse_chunk(chunks + chunkID);
  37.   destroy_input_chunks(chunks);
  38.   return 0;
  39. }
  40.  
  41. struct chunk * get_input_chunks (void) {
  42.   struct chunk * chunks = NULL;
  43.   unsigned chunk_count = 0;
  44.   char * line = get_line();
  45.   struct chunk current_chunk = {.type = 0, .contents = NULL};
  46.   unsigned c4 = 0; // current chunk contents count
  47.   unsigned line_length;
  48.   while (line) {
  49.     line_length = strlen(line);
  50.     if (*line == current_chunk.type) {
  51.       if (!current_chunk.type) {
  52.         line = get_line();
  53.         continue;
  54.       }
  55.       *line = '\n';
  56.       if (c4) {
  57.         current_chunk.contents = realloc(current_chunk.contents, c4 + line_length);
  58.         memcpy(current_chunk.contents + c4, line, line_length);
  59.         c4 += line_length;
  60.       } else {
  61.         current_chunk.contents = malloc(c4 = line_length - 1);
  62.         memcpy(current_chunk.contents, line + 1, c4);
  63.       }
  64.       free(line);
  65.       line = get_line();
  66.       continue;
  67.     }
  68.     if (current_chunk.type) {
  69.       c4[current_chunk.contents = realloc(current_chunk.contents, c4 + 1)] = 0;
  70.       chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1));
  71.       chunks[chunk_count ++] = current_chunk;
  72.     }
  73.     current_chunk.type = *line;
  74.     current_chunk.contents = NULL;
  75.     c4 = 0;
  76.   }
  77.   if (current_chunk.type) {
  78.     chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1));
  79.     chunks[chunk_count ++] = current_chunk;
  80.   }
  81.   chunk_count[chunks = realloc(chunks, sizeof(struct chunk) * (chunk_count + 1))] = (struct chunk) {.type = 0, .contents = NULL};
  82.   return chunks;
  83. }
  84.  
  85. char * get_line (void) {
  86.   char * line = NULL;
  87.   unsigned length = 0;
  88.   int next_char = getchar();
  89.   if (next_char == EOF) return NULL;
  90.   while ((next_char != EOF) && (next_char != '\n')) {
  91.     line = realloc(line, length + 1);
  92.     line[length ++] = next_char;
  93.     next_char = getchar();
  94.   }
  95.   line = realloc(line, length + 1);
  96.   line[length] = 0;
  97.   return line;
  98. }
  99.  
  100. void destroy_input_chunks (struct chunk * chunks) {
  101.   struct chunk * chunk;
  102.   for (chunk = chunks; chunk -> type; chunk ++) free(chunk -> contents);
  103.   free(chunks);
  104. }
  105.  
  106. void parse_chunk (const struct chunk * chunk) {
  107.   if (!chunk) return;
  108.   switch (chunk -> type) {
  109.     case ' ':
  110.       printf("%s\n", chunk -> contents);
  111.       return;
  112.     case '%':
  113.       display_percentage_table(chunk -> contents);
  114.       return;
  115.     case '!':
  116.       if (database) free(database);
  117.       database = strcpy(malloc(strlen(chunk -> contents) + 1), chunk -> contents);
  118.       return;
  119.     case 0:
  120.       return;
  121.     default:
  122.       fprintf(stderr, "unknown chunk type: %c\n", chunk -> type);
  123.   }
  124. }
  125.  
  126. unsigned execute_query (const char * query, char **** results, unsigned * column_count) {
  127.   if (!(query && database)) goto argument_error;
  128.   char * arguments[4];
  129.   *arguments = "mysql";
  130.   arguments[1] = "-sr";
  131.   arguments[2] = database;
  132.   arguments[3] = NULL;
  133.   char * output;
  134.   unsigned output_size = execute_program(*arguments, arguments, query, strlen(query), (void **) &output);
  135.   if (!(output && output_size)) goto output_error;
  136.   output = realloc(output, 1 + output_size);
  137.   output[output_size] = 0;
  138.   char * buffer = output;
  139.   char * line = next_output_line(&buffer);
  140.   char * first_line = line;
  141.   if (!(line && *line)) goto output_error;
  142.   unsigned col_count = 1;
  143.   while (first_line = strchr(first_line, '\t')) first_line ++, col_count ++;
  144.   if (column_count) *column_count = col_count;
  145.   unsigned row_count = 0;
  146.   if (!results) {
  147.     while (buffer = strchr(buffer, '\n')) buffer ++, row_count ++;
  148.     free(output);
  149.     return row_count;
  150.   }
  151.   *results = NULL;
  152.   char *** row;
  153.   unsigned col;
  154.   unsigned len;
  155.   char * tabpos;
  156.   while (line) {
  157.     row = (*results = realloc(*results, sizeof(char **) * (1 + row_count))) + row_count;
  158.     row_count ++;
  159.     *row = calloc(col_count + 1, sizeof(char *));
  160.     for (col = 0; col < col_count; col ++) {
  161.       if (!line) break;
  162.       len = (tabpos = strchr(line, '\t')) ? tabpos - line : strlen(line);
  163.       memcpy(col[*row] = malloc(len + 1), line, len);
  164.       (*row)[col][len] = 0;
  165.       line = tabpos;
  166.       if (tabpos) line ++;
  167.     }
  168.     line = next_output_line(&buffer);
  169.   }
  170.   row_count[*results = realloc(*results, sizeof(char **) * (1 + row_count))] = NULL;
  171.   free(output);
  172.   return row_count;
  173.   output_error:
  174.   free(output);
  175.   argument_error:
  176.   if (results) *results = NULL;
  177.   if (column_count) *column_count = 0;
  178.   return 0;
  179. }
  180.  
  181. char * next_output_line (char ** string) {
  182.   if (!(string && *string && **string)) return NULL;
  183.   unsigned line_length = strcspn(*string, "\r\n");
  184.   char * result = *string;
  185.   *string += line_length + 1 + !strncmp(*string + line_length, "\r\n", 2);
  186.   result[line_length] = 0;
  187.   return result;
  188. }
  189.  
  190. void destroy_query_results (char *** results) {
  191.   char *** row;
  192.   char ** cell;
  193.   for (row = results; *row; row ++) {
  194.     for (cell = *row; *cell; cell ++) free(*cell);
  195.     free(*row);
  196.   }
  197.   free(results);
  198. }
  199.  
  200. unsigned execute_program (const char * program, char ** arguments, const void * input, unsigned input_size, void ** output) {
  201.   int file_descriptors[4];
  202.   pipe(file_descriptors);
  203.   pipe(file_descriptors + 2);
  204.   fcntl(file_descriptors[1], F_SETFD, FD_CLOEXEC);
  205.   fcntl(file_descriptors[2], F_SETFD, FD_CLOEXEC);
  206.   int pid = fork();
  207.   if (!pid) execute_subprocess(program, arguments, *file_descriptors, file_descriptors[3]);
  208.   close(*file_descriptors);
  209.   close(file_descriptors[3]);
  210.   if (input) write(file_descriptors[1], input, input_size);
  211.   close(file_descriptors[1]);
  212.   char buffer[4096];
  213.   unsigned output_size = 0;
  214.   int reception;
  215.   if (output) *output = NULL;
  216.   while (reception = read(file_descriptors[2], buffer, 4096)) {
  217.     if (reception < 0) {
  218.       if ((errno == EAGAIN) || (errno == EINTR)) continue;
  219.       break;
  220.     }
  221.     if (output) {
  222.       *output = realloc(*output, output_size + reception);
  223.       memcpy(((char *) *output) + output_size, buffer, reception);
  224.     }
  225.     output_size += reception;
  226.   }
  227.   close(file_descriptors[2]);
  228.   waitpid(pid, NULL, 0);
  229.   return output_size;
  230. }
  231.  
  232. void execute_subprocess (const char * program, char ** arguments, int read_fd, int write_fd) {
  233.   // must not return to caller
  234.   while (dup2(read_fd, 0) < 0);
  235.   close(read_fd);
  236.   while (dup2(write_fd, 1) < 0);
  237.   close(write_fd);
  238.   execvp(program, arguments);
  239.   abort();
  240. }
  241.  
  242. #define PERCENTAGE_TABLE_ROW "[tr][td=width: 270px; border-bottom: 1px solid #ccc; padding-left: 6px]%s[/td]\
  243.                               [td=width: 80px; text-align: right; border-bottom: 1px solid #ccc]%s[/td]\
  244.                               [td=width: 50px; text-align: right; border-bottom: 1px solid #ccc; padding-right: 6px]%s[/td][/tr]"
  245.  
  246. #define PERCENTAGE_TABLE_TOTAL_ROW "[tr][td=width: 270px; text-align: right; font-weight: bold]Total[/td]\
  247.                                     [td=width: 80px; text-align: right]%s[/td]\
  248.                                     [td=width: 50px; text-align: right; padding-right: 6px; font-weight: bold]100[/td][/tr]"
  249.  
  250. void display_percentage_table (const char * query) {
  251.   char *** results;
  252.   unsigned column_count;
  253.   unsigned row_count = execute_query(query, &results, &column_count);
  254.   if (column_count != 2) fputs("warning: percentage query did not return two columns\n", stderr);
  255.   if (!row_count) {
  256.     destroy_query_results(results);
  257.     return;
  258.   }
  259.   unsigned long long * item_counts = malloc(row_count * sizeof(unsigned long long));
  260.   unsigned row;
  261.   char * t;
  262.   unsigned long long total = 0;
  263.   for (row = 0; row < row_count; row ++) {
  264.     item_counts[row] = strtoull(results[row][1], &t, 10);
  265.     if (*t) fprintf(stderr, "warning: illegal numeric value at row %u: %s\n", row + 1, results[row][1]);
  266.     total += item_counts[row];
  267.   }
  268.   unsigned short * percentages = malloc(row_count * sizeof(unsigned short));
  269.   unsigned short total_percentages = 0;
  270.   for (row = 0; row < row_count; row ++) {
  271.     percentages[row] = (item_counts[row] * 20000 / total + 1) >> 1;
  272.     total_percentages += percentages[row];
  273.   }
  274.   unsigned short max = *percentages;
  275.   unsigned max_row = 0;
  276.   for (row = 1; row < row_count; row ++) if (percentages[row] > max) {
  277.     max_row = row;
  278.     max = percentages[row];
  279.   }
  280.   percentages[max_row] = percentages[max_row] + 10000 - total_percentages;
  281.   puts("[table]");
  282.   for (row = 0; row < row_count; row ++)
  283.     printf(PERCENTAGE_TABLE_ROW, results[row][0], convert_number_to_string(item_counts[row]), convert_percentage_to_string(percentages[row]));
  284.   printf(PERCENTAGE_TABLE_TOTAL_ROW, convert_number_to_string(total));
  285.   puts("[/table]\n");
  286.   destroy_query_results(results);
  287.   free(item_counts);
  288.   free(percentages);
  289. }
  290.  
  291. char * convert_number_to_string (unsigned long long number) {
  292.   if (number < 1000) {
  293.     sprintf(last_number, "%llu", number);
  294.     return last_number;
  295.   }
  296.   convert_number_to_string(number / 1000);
  297.   char remainder[5];
  298.   sprintf(remainder, ",%03llu", number % 1000);
  299.   strcat(last_number, remainder);
  300.   return last_number;
  301. }
  302.  
  303. char * convert_percentage_to_string (unsigned short percentage) {
  304.   if (percentage > 9999) {
  305.     strcpy(last_percentage, "100");
  306.     return last_percentage;
  307.   }
  308.   sprintf(last_percentage, "%2hu.%02hu", percentage / 100, percentage % 100);
  309.   return last_percentage;
  310. }
Advertisement
RAW Paste Data Copied
Advertisement