# Final 參考答案 ## 1. Motion BMP ## 2. CPE :::spoiler fin02.c ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #include <curl/curl.h> #define MAX_LINE_LENGTH 8 * 1024 * 1024 // 8MB #define WIDTH 800 #define HEIGHT 800 #define CENTER_X (WIDTH / 2) #define CENTER_Y (HEIGHT / 2) #define RADIUS (WIDTH / 3) typedef struct { unsigned char b; unsigned char g; unsigned char r; } Pixel; typedef struct { unsigned int width; unsigned int height; Pixel *pixels; } Bitmap; typedef struct { char *data; size_t size; } MemoryBlock; void save_bmp(const char *filename, Bitmap *bmp) { FILE *f; unsigned char bmpfileheader[14] = { 'B', 'M', // BMP signature 0, 0, 0, 0, // File size 0, 0, // Reserved 0, 0, // Reserved 54, 0, 0, 0 // Data offset }; unsigned char bmpinfoheader[40] = { 40, 0, 0, 0, // Info header size 0, 0, 0, 0, // Width 0, 0, 0, 0, // Height 1, 0, // Planes 24, 0, // Bits per pixel 0, 0, 0, 0, // Compression 0, 0, 0, 0, // Image size (can be 0 for BI_RGB) 0, 0, 0, 0, // X pixels per meter 0, 0, 0, 0, // Y pixels per meter 0, 0, 0, 0, // Total colors 0, 0, 0, 0 // Important colors }; unsigned int filesize = 54 + 3 * bmp->width * bmp->height; bmpfileheader[2] = (unsigned char)(filesize); bmpfileheader[3] = (unsigned char)(filesize >> 8); bmpfileheader[4] = (unsigned char)(filesize >> 16); bmpfileheader[5] = (unsigned char)(filesize >> 24); bmpinfoheader[4] = (unsigned char)(bmp->width); bmpinfoheader[5] = (unsigned char)(bmp->width >> 8); bmpinfoheader[6] = (unsigned char)(bmp->width >> 16); bmpinfoheader[7] = (unsigned char)(bmp->width >> 24); bmpinfoheader[8] = (unsigned char)(bmp->height); bmpinfoheader[9] = (unsigned char)(bmp->height >> 8); bmpinfoheader[10] = (unsigned char)(bmp->height >> 16); bmpinfoheader[11] = (unsigned char)(bmp->height >> 24); f = fopen(filename, "wb"); fwrite(bmpfileheader, 1, 14, f); fwrite(bmpinfoheader, 1, 40, f); for (int i = 0; i < bmp->height; i++) { fwrite(bmp->pixels + (bmp->width * (bmp->height - i - 1)), 3, bmp->width, f); } fclose(f); } void draw_pie_segment(Bitmap *bmp, double start_angle, double end_angle, Pixel color) { for (int y = 0; y < bmp->height; y++) { for (int x = 0; x < bmp->width; x++) { int dx = x - CENTER_X; int dy = y - CENTER_Y; double distance = sqrt(dx * dx + dy * dy); if (distance <= RADIUS) { double angle = -atan2(dy, dx) * 180 / M_PI - 90; if (angle < 0) angle += 360; if (angle >= start_angle && angle <= end_angle) { bmp->pixels[y * bmp->width + x] = color; } } } } } size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; MemoryBlock *mem = (MemoryBlock *)userp; char *ptr = realloc(mem->data, mem->size + realsize + 1); if(ptr == NULL) { // out of memory return 0; } mem->data = ptr; memcpy(&(mem->data[mem->size]), contents, realsize); mem->size += realsize; mem->data[mem->size] = 0; return realsize; } int fetch_data(const char *url, MemoryBlock *mem) { CURL *curl_handle; CURLcode res; mem->data = malloc(1); // will be grown as needed by realloc mem->size = 0; // no data at this point curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)mem); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl_handle); if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return 1; } curl_easy_cleanup(curl_handle); curl_global_cleanup(); return 0; } void parse_and_count_scores(const char *data, int score_count[8]) { const char *pos = data; pos = strstr(pos, "var resultList ="); if (pos == NULL) { fprintf(stderr, "Error: 'var resultList =' not found in data\n"); return; } // Move to the next line pos = strchr(pos, '\n'); if (pos == NULL) { return; } // Find all occurrences of "SCORE":"N" where N is a one-digit number (0-7) while ((pos = strstr(pos, "\"SCORE\":\"")) != NULL) { pos += 9; // Move past "\"SCORE\":\"" if (isdigit(*pos)) { int score = *pos - '0'; if (score >= 0 && score <= 7) { score_count[score]++; } } pos++; } } void print_usage() { printf("Usage: fin02 date\n"); printf("-h, --help Display this information and exit.\n"); } int main(int argc, char *argv[]) { if (argc != 2) { print_usage(); return EXIT_FAILURE; } if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { print_usage(); return EXIT_SUCCESS; } char url[256]; snprintf(url, sizeof(url), "https://cpe.cse.nsysu.edu.tw/cpe/scoreboard/%s", argv[1]); MemoryBlock mem; if (fetch_data(url, &mem) != 0) { fprintf(stderr, "Failed to fetch data from %s\n", url); return EXIT_FAILURE; } int score_count[8] = {0}; parse_and_count_scores(mem.data, score_count); free(mem.data); // Calculate total scores int total_scores = 0; for (int i = 0; i <= 7; i++) { total_scores += score_count[i]; } // Print the score counts and percentages for (int i = 0; i <= 7; i++) { if (total_scores > 0) { printf("%d: %d (%.2f%%)\n", i, score_count[i], (score_count[i] / (double)total_scores) * 100); } else { printf("%d: %d (0.00%%)\n", i, score_count[i]); } } // Create a bitmap for the pie chart Bitmap bmp; bmp.width = WIDTH; bmp.height = HEIGHT; bmp.pixels = (Pixel *)malloc(WIDTH * HEIGHT * sizeof(Pixel)); if (bmp.pixels == NULL) { perror("Error allocating memory for bitmap"); return EXIT_FAILURE; } // Fill the bitmap with white color for (int i = 0; i < WIDTH * HEIGHT; i++) { bmp.pixels[i].r = 255; bmp.pixels[i].g = 255; bmp.pixels[i].b = 255; } // Colors for the pie segments Pixel colors[8] = { {255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 0}, {0, 255, 255}, {255, 0, 255}, {128, 0, 0}, {0, 128, 0} }; // Draw the pie chart segments double start_angle = 0.0; for (int i = 0; i <= 7; i++) { if (score_count[i] > 0) { double end_angle = start_angle + (score_count[i] / (double)total_scores) * 360.0; draw_pie_segment(&bmp, start_angle, end_angle, colors[i]); start_angle = end_angle; } } // Save the bitmap to a file save_bmp("output.bmp", &bmp); // Free allocated memory free(bmp.pixels); return EXIT_SUCCESS; } ``` ::: ## 3. Database Credit: 41171114H 李O歆 :::spoiler fin03.c ```c= #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "database.h" #include "linuxlist.h" const char *COLUMNS[] = {"FirstName","LastName", "phone", "year"}; const size_t COLUMNS_SIZE = sizeof(COLUMNS) / sizeof(COLUMNS[0]); static inline void INIT_LIST_HEAD(struct list_head *head) { head->next = head; head->prev = head; } struct list_head *new_column_list(const char *columns[], int columns_size) { struct list_head *column_list = (struct list_head *)malloc(sizeof(struct list_head)); if (!column_list) { return NULL; } INIT_LIST_HEAD(column_list); for (size_t i = 0; i < columns_size; i++) { sLabel *label = (sLabel *)malloc(sizeof(sLabel)); if (!label) { return NULL; } label->pStr = strdup(columns[i]); list_add_tail(&label->list, column_list); } return column_list; } int main() { struct list_head *column_list = new_column_list(COLUMNS, COLUMNS_SIZE); if (!column_list) { return EXIT_FAILURE; } if (setup_table(column_list) == -1) { return EXIT_FAILURE; } struct list_head *record_list = (struct list_head *)malloc(sizeof(struct list_head)); if (!record_list) { return EXIT_FAILURE; } INIT_LIST_HEAD(record_list); while (1) { printf("Current table size: %d\n", get_size(record_list)); printf("Do you want to add a record? (y/n): "); char c; scanf("%c", &c); fgetc(stdin); if (c != 'y') { break; } sRecord *record = (sRecord *)malloc(sizeof(sRecord)); if (!record) { return EXIT_FAILURE; } INIT_LIST_HEAD(&record->data); INIT_LIST_HEAD(&record->list); for (size_t i = 0; i < COLUMNS_SIZE; i++) { sItem *item = (sItem *)malloc(sizeof(sItem)); if (!item) { return EXIT_FAILURE; } printf("%s: ", COLUMNS[i]); item->pData = (char *)malloc(256); if (!item->pData) { return EXIT_FAILURE; } scanf("%[^\n]", item->pData); fgetc(stdin); list_add_tail(&item->next, &record->data); } if (add(record_list, record) == -1) { return EXIT_FAILURE; } } while (1) { printf("Do you want to query the database? (y/n): "); char c; scanf("%c", &c); fgetc(stdin); if (c != 'y') { break; } char cmd[256]; printf("Enter the query command: "); scanf("%[^\n]", cmd); fgetc(stdin); struct list_head *result_list = (struct list_head *)malloc(sizeof(struct list_head)); if (!result_list) { return EXIT_FAILURE; } INIT_LIST_HEAD(result_list); int32_t result_count = query(result_list, record_list, cmd); printf("Query Result count: %d\n", result_count); struct list_head *pos; list_for_each(pos, result_list) { sRecord *record = list_entry(pos, sRecord, list); struct list_head *pos_item; size_t idx = 0; list_for_each(pos_item, &record->data) { sItem *item = list_entry(pos_item, sItem, next); printf("%s: %s, ", COLUMNS[idx], item->pData); idx++; } printf("\n"); } } return EXIT_SUCCESS; } ``` ::: :::spoiler database.c ```c= # include <stdio.h> # include <stdint.h> # include <string.h> # include <stdlib.h> # include "database.h" // 確保不會再次定義 INIT_LIST_HEAD #ifndef INIT_LIST_HEAD #define INIT_LIST_HEAD(ptr) ((ptr)->next = (ptr), (ptr)->prev = (ptr)) #endif static struct list_head label_list; int32_t setup_table(const struct list_head *pLabelList) { INIT_LIST_HEAD(&label_list); struct list_head *pos; list_for_each(pos, pLabelList) { sLabel *label = list_entry(pos, sLabel, list); sLabel *new_label = (sLabel *)malloc(sizeof(sLabel)); if (!new_label) { return -1; } new_label->pStr = strdup(label->pStr); list_add_tail(&new_label->list, &label_list); } return 0; } int32_t add(struct list_head *pRecordList, sRecord *pRecord) { if (!pRecordList || !pRecord) { return -1; } list_add_tail(&pRecord->list, pRecordList); return 0; } int32_t get_size(struct list_head *pRecordList) { if (!pRecordList) { return -1; } int count = 0; struct list_head *pos; list_for_each(pos, pRecordList) { count++; } return count; } int32_t query(struct list_head *pResultList, struct list_head *pRecordList, char *pCmd) { if (!pResultList || !pRecordList || !pCmd) { return -1; } char *select_part = strtok(pCmd, " "); if (strcmp(select_part, "SELECT") != 0) { return -1; } char *labels = strtok(NULL, "WHERE"); char *conditions = strtok(NULL, ""); if (!labels || !conditions) { return -1; } char *label_tokens[10]; int label_count = 0; char *label = strtok(labels, ","); while (label) { label_tokens[label_count++] = label; label = strtok(NULL, ","); } struct list_head *record_pos; list_for_each(record_pos, pRecordList) { sRecord *record = list_entry(record_pos, sRecord, list); int match = 1; char *condition = strtok(conditions, " "); while (condition) { char *key = strtok(condition, "="); char *value = strtok(NULL, "="); if (key && value) { value[strlen(value) - 1] = '\0'; // Remove trailing double quote value++; // Remove leading double quote struct list_head *data_pos; int found = 0; list_for_each(data_pos, &record->data) { sItem *item = list_entry(data_pos, sItem, next); if (strcmp(key, item->pData) == 0 && strcmp(value, item->pData) == 0) { found = 1; break; } } if (!found) { match = 0; break; } } condition = strtok(NULL, " "); } if (match) { sRecord *result_record = (sRecord *)malloc(sizeof(sRecord)); INIT_LIST_HEAD(&result_record->data); INIT_LIST_HEAD(&result_record->list); struct list_head *data_pos; list_for_each(data_pos, &record->data) { sItem *item = list_entry(data_pos, sItem, next); for (int i = 0; i < label_count; i++) { if (strcmp(label_tokens[i], item->pData) == 0) { sItem *new_item = (sItem *)malloc(sizeof(sItem)); new_item->pData = strdup(item->pData); list_add_tail(&new_item->next, &result_record->data); } } } list_add_tail(&result_record->list, pResultList); } } int result_count = 0; struct list_head *pos; list_for_each(pos, pResultList) { result_count++; } return result_count; } ``` ::: :::spoiler linuxlist.h ```c= #ifndef _LINUX_LIST_H #define _LINUX_LIST_H struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = (void *) 0; entry->prev = (void *) 0; } static inline int list_empty(const struct list_head *head) { return head->next == head; } #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #endif ``` :::