# 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
```
:::