From 01790a0f391e4f868de34a1c55a8d63d9b7146da Mon Sep 17 00:00:00 2001 From: b0rbor4d Date: Wed, 15 Apr 2026 00:18:50 +0200 Subject: [PATCH] Add Final Project: Todo-App in C --- exercises/final_project/todo_c/todo.c | 263 ++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 exercises/final_project/todo_c/todo.c diff --git a/exercises/final_project/todo_c/todo.c b/exercises/final_project/todo_c/todo.c new file mode 100644 index 0000000..e70a601 --- /dev/null +++ b/exercises/final_project/todo_c/todo.c @@ -0,0 +1,263 @@ +// ============================================ +// Final-Projekt: Todo-App in C +// Ein vollständiges CLI-Programm mit CRUD-Operationen +// ============================================ + +#include +#include +#include +#include +#include + +#define MAX_TODOS 100 +#define MAX_TITLE 100 +#define MAX_DESC 500 +#define DATA_FILE "todos.dat" + +// Datenstruktur für ein Todo +typedef struct { + int id; + char title[MAX_TITLE]; + char description[MAX_DESC]; + bool completed; + time_t created_at; +} Todo; + +// Datenstruktur für die Todo-Liste +typedef struct { + Todo items[MAX_TODOS]; + int count; +} TodoList; + +// Funktionsprototypen +void todo_init(TodoList* list); +void todo_add(TodoList* list, const char* title, const char* description); +void todo_list(const TodoList* list); +void todo_complete(TodoList* list, int id); +void todo_delete(TodoList* list, int id); +void todo_save(const TodoList* list); +void todo_load(TodoList* list); +void print_menu(void); +void clear_input_buffer(void); + +// ============================================ +// IMPLEMENTATION +// ============================================ + +void todo_init(TodoList* list) { + list->count = 0; + for (int i = 0; i < MAX_TODOS; i++) { + list->items[i].id = 0; + list->items[i].title[0] = '\0'; + list->items[i].description[0] = '\0'; + list->items[i].completed = false; + list->items[i].created_at = 0; + } +} + +void todo_add(TodoList* list, const char* title, const char* description) { + if (list->count >= MAX_TODOS) { + printf("Fehler: Maximale Anzahl erreicht!\n"); + return; + } + + Todo* new_todo = &list->items[list->count]; + new_todo->id = list->count + 1; + strncpy(new_todo->title, title, MAX_TITLE - 1); + new_todo->title[MAX_TITLE - 1] = '\0'; + strncpy(new_todo->description, description, MAX_DESC - 1); + new_todo->description[MAX_DESC - 1] = '\0'; + new_todo->completed = false; + new_todo->created_at = time(NULL); + + list->count++; + printf("Todo #%d hinzugefügt!\n", new_todo->id); +} + +void todo_list(const TodoList* list) { + if (list->count == 0) { + printf("Keine Todos vorhanden.\n"); + return; + } + + printf("\n=== DEINE TODOS ===\n"); + printf("%-4s | %-10s | %-30s | %-20s\n", "ID", "Status", "Titel", "Erstellt"); + printf("-----+------------+--------------------------------+--------------------\n"); + + for (int i = 0; i < list->count; i++) { + const Todo* todo = &list->items[i]; + char date_str[26]; + ctime_r(&todo->created_at, date_str); + date_str[24] = '\0'; // Remove newline + + const char* status = todo->completed ? "[✓] Done " : "[ ] Open "; + printf("%-4d | %s | %-30s | %.20s\n", + todo->id, status, todo->title, date_str); + + if (strlen(todo->description) > 0) { + printf(" | | %s\n", todo->description); + } + } + printf("\n"); +} + +void todo_complete(TodoList* list, int id) { + for (int i = 0; i < list->count; i++) { + if (list->items[i].id == id) { + list->items[i].completed = !list->items[i].completed; + printf("Todo #%d %s!\n", id, + list->items[i].completed ? "abgeschlossen" : "wieder geöffnet"); + return; + } + } + printf("Fehler: Todo #%d nicht gefunden!\n", id); +} + +void todo_delete(TodoList* list, int id) { + int found_index = -1; + + for (int i = 0; i < list->count; i++) { + if (list->items[i].id == id) { + found_index = i; + break; + } + } + + if (found_index == -1) { + printf("Fehler: Todo #%d nicht gefunden!\n", id); + return; + } + + // Array komprimieren + for (int i = found_index; i < list->count - 1; i++) { + list->items[i] = list->items[i + 1]; + list->items[i].id = i + 1; // ID neu zuweisen + } + + list->count--; + printf("Todo #%d gelöscht!\n", id); +} + +void todo_save(const TodoList* list) { + FILE* file = fopen(DATA_FILE, "wb"); + if (!file) { + printf("Fehler: Konnte Datei nicht öffnen!\n"); + return; + } + + fwrite(&list->count, sizeof(int), 1, file); + fwrite(list->items, sizeof(Todo), list->count, file); + + fclose(file); + printf("Todos gespeichert!\n"); +} + +void todo_load(TodoList* list) { + FILE* file = fopen(DATA_FILE, "rb"); + if (!file) { + printf("Keine gespeicherte Daten gefunden. Starte neu...\n"); + return; + } + + fread(&list->count, sizeof(int), 1, file); + fread(list->items, sizeof(Todo), list->count, file); + + fclose(file); + printf("%d Todos geladen!\n", list->count); +} + +void print_menu(void) { + printf("\n=== TODO-APP ===\n"); + printf("1. Todo hinzufügen\n"); + printf("2. Todos anzeigen\n"); + printf("3. Todo abschließen/öffnen\n"); + printf("4. Todo löschen\n"); + printf("5. Speichern\n"); + printf("6. Beenden\n"); + printf("Wahl: "); +} + +void clear_input_buffer(void) { + int c; + while ((c = getchar()) != '\n' && c != EOF); +} + +// ============================================ +// HAUPTPROGRAMM +// ============================================ +int main(void) { + TodoList list; + todo_init(&list); + todo_load(&list); + + int choice; + char title[MAX_TITLE]; + char description[MAX_DESC]; + int id; + + printf("\n📝 Willkommen zur Todo-App!\n"); + + while (true) { + print_menu(); + + if (scanf("%d", &choice) != 1) { + clear_input_buffer(); + printf("Ungültige Eingabe!\n"); + continue; + } + clear_input_buffer(); + + switch (choice) { + case 1: + printf("Titel: "); + fgets(title, MAX_TITLE, stdin); + title[strcspn(title, "\n")] = '\0'; // Remove newline + + printf("Beschreibung (optional): "); + fgets(description, MAX_DESC, stdin); + description[strcspn(description, "\n")] = '\0'; + + todo_add(&list, title, description); + break; + + case 2: + todo_list(&list); + break; + + case 3: + printf("Todo-ID: "); + if (scanf("%d", &id) == 1) { + todo_complete(&list, id); + } + clear_input_buffer(); + break; + + case 4: + printf("Todo-ID: "); + if (scanf("%d", &id) == 1) { + todo_delete(&list, id); + } + clear_input_buffer(); + break; + + case 5: + todo_save(&list); + break; + + case 6: + printf("Speichern vor Beenden? (j/n): "); + char save_choice; + scanf(" %c", &save_choice); + if (save_choice == 'j' || save_choice == 'J') { + todo_save(&list); + } + printf("Auf Wiedersehen!\n"); + return 0; + + default: + printf("Ungültige Wahl!\n"); + } + } + + return 0; +}