Add Final Project: Todo-App in C
This commit is contained in:
263
exercises/final_project/todo_c/todo.c
Normal file
263
exercises/final_project/todo_c/todo.c
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
// ============================================
|
||||||
|
// Final-Projekt: Todo-App in C
|
||||||
|
// Ein vollständiges CLI-Programm mit CRUD-Operationen
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user