Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #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;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement