From fa0ee597cedf3e082c36a936b772df16b9c8d1cb Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Mon, 10 Nov 2025 04:25:23 +0100 Subject: [PATCH] migrate to c --- .clangd | 2 + app.cpp => app.c | 274 ++++++++++++++++++++++++----------------------- build | 2 +- djstdlib | 2 +- 4 files changed, 144 insertions(+), 136 deletions(-) rename app.cpp => app.c (75%) diff --git a/.clangd b/.clangd index e94be6c..65215ff 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,5 @@ CompileFlags: Add: + - -std=c99 + - -xc - -DOS_LINUX diff --git a/app.cpp b/app.c similarity index 75% rename from app.cpp rename to app.c index ef6ea50..a0682d6 100644 --- a/app.cpp +++ b/app.c @@ -1,34 +1,31 @@ -#include -#include -#include -#include "./djstdlib/core.cpp" -#include "djstdlib/core.h" +#include "time.h" +#include "djstdlib/core.c" -enum CmdArgType { +typedef enum { CmdArgType_BOOL, CmdArgType_STRING, CmdArgType_INT, CmdArgType_FLOAT, // -- CmdArgType_Count, -}; +} CmdArgType; string cmd_argTypeFmt(CmdArgType type) { switch (type) { case CmdArgType_FLOAT: - return "float"_s; + return s("float"); case CmdArgType_BOOL: - return "boolean flag"_s; + return s("boolean flag"); case CmdArgType_INT: - return "integer"_s; + return s("integer"); case CmdArgType_STRING: - return "string"_s; + return s("string"); default: - return "invalid command argument type"_s; + return s("invalid command argument type"); } } -struct CmdOptionArg { +typedef struct { /** * The zero byte '\0' means no char name. */ @@ -36,41 +33,45 @@ struct CmdOptionArg { string name; string description; CmdArgType type; -}; +} CmdOptionArg; -struct CmdPositionalArg { +typedef struct { string name; string description; CmdArgType type; -}; +} CmdPositionalArg; -struct CmdParsedOptionArg { +typedef struct { string name; string content; CmdArgType type; -}; +} CmdParsedOptionArg; -struct CmdParsedPositionalArg { +typedef struct { int index; CmdArgType type; void *content; -}; +} CmdParsedPositionalArg; -struct ParsedCmd { - list posArgs; - list optArgs; -}; +DefineList(CmdParsedPositionalArg, CmdParsedPositionalArg); +DefineList(CmdParsedOptionArg, CmdParsedOptionArg); +typedef struct { + CmdParsedPositionalArgList posArgs; + CmdParsedOptionArgList optArgs; +} ParsedCmd; -struct BasicCommand { +DefineList(CmdPositionalArg, CmdPositionalArg); +DefineList(CmdOptionArg, CmdOptionArg); +typedef struct { string name; string description; - list posArgs; - list optArgs; + CmdPositionalArgList posArgs; + CmdOptionArgList optArgs; /** * @returns The status code of the command */ - int32 (*command)(Arena *arena, list args); -}; + int32 (*command)(Arena *arena, StringList args); +} BasicCommand; void cmd_printSyntax(BasicCommand *cmd) { print("%S", cmd->name); @@ -79,7 +80,8 @@ void cmd_printSyntax(BasicCommand *cmd) { } } -void cmd_printHelp(Arena *arena, list commands, string *helpCmd) { +DefineList(BasicCommand, BasicCommand); +void cmd_printHelp(Arena *arena, BasicCommandList commands, string *helpCmd) { if (helpCmd) { for (EachIn(commands, i)) { BasicCommand *icmd = &commands.data[i]; @@ -98,7 +100,7 @@ void cmd_printHelp(Arena *arena, list commands, string *helpCmd) { print("Options:\n"); for (EachIn(icmd->optArgs, j)) { CmdOptionArg *optArg = &icmd->optArgs.data[j]; - string charNameStr = optArg->charName != '\0' ? strPrintf(arena, "-%c, ", optArg->charName) : ""_s; + string charNameStr = optArg->charName != '\0' ? strPrintf(arena, "-%c, ", optArg->charName) : s(""); print("%S--%S (%S) - %S\n", charNameStr, optArg->name, cmd_argTypeFmt(optArg->type), optArg->description); } } @@ -113,47 +115,48 @@ void cmd_printHelp(Arena *arena, list commands, string *helpCmd) { } } -const string LOG_FILE_LOCATION = "./log.gtl"_s; -const string DB_FILE_LOCATION = "./db.gtd"_s; +const string LOG_FILE_LOCATION = s("./log.gtl"); +const string DB_FILE_LOCATION = s("./db.gtd"); -struct GymLogDbHeader { +typedef struct { uint32 nextId; -}; +} GymLogDbHeader; -struct GymLogDbEntry { +typedef struct { uint32 id; uint32 nameLength; -}; +} GymLogDbEntry; -struct GymLogDbParsedEntry { +typedef struct { uint32 id; string name; -}; +} GymLogDbParsedEntry; typedef GymLogDbParsedEntry Exercise; -struct GymLogDbParsed { +DefineList(GymLogDbParsedEntry, GymLogDbParsedEntry); +typedef struct { GymLogDbHeader header; - list entries; -}; + GymLogDbParsedEntryList entries; +} GymLogDbParsed; -struct WeightRepsInfo { +typedef struct { uint8 reps; real32 weight; -}; +} WeightRepsInfo; -struct GymLogEntry { +typedef struct { uint64 timestamp; uint32 exerciseId; union { WeightRepsInfo weightRepsInfo; }; -}; +} GymLogEntry; -struct WorkSummary { +typedef struct { real32 totalWork; uint32 restTime; -}; +} WorkSummary; GymLogDbParsed *parseDb(Arena *arena, string database) { GymLogDbParsed *dbParsed = PushStruct(arena, GymLogDbParsed); @@ -162,35 +165,36 @@ GymLogDbParsed *parseDb(Arena *arena, string database) { size_t head = sizeof(GymLogDbHeader); uint32 entriesLeft = dbParsed->header.nextId - 1; - dbParsed->entries = PushList(arena, GymLogDbParsedEntry, entriesLeft); + dbParsed->entries = PushList(arena, GymLogDbParsedEntryList, entriesLeft); while (entriesLeft > 0 && head < database.length) { GymLogDbEntry *currentEntry = (GymLogDbEntry *)((byte *)database.str + head); GymLogDbParsedEntry parsedEntry = { currentEntry->id, PushString(arena, currentEntry->nameLength) }; head += sizeof(GymLogDbEntry); memcpy(parsedEntry.name.str, database.str + head, currentEntry->nameLength); - appendList(&dbParsed->entries, parsedEntry); + AppendList(&dbParsed->entries, parsedEntry); head += currentEntry->nameLength; } return dbParsed; } -list loadEntryLog(Arena *arena, string fileLocation) { - list result = {0}; +DefineList(GymLogEntry, GymLogEntry); +GymLogEntryList loadEntryLog(Arena *arena, string fileLocation) { + GymLogEntryList result = {0}; string logfile = os_readEntireFile(arena, LOG_FILE_LOCATION); if (logfile.length % sizeof(GymLogEntry) != 0) { print("Log file corrupted.\n"); } else { size_t entryCount = logfile.length / sizeof(GymLogEntry); - result = { (GymLogEntry *)logfile.str, entryCount, entryCount }; + result = (GymLogEntryList){ (GymLogEntry *)logfile.str, entryCount, entryCount }; } return result; } -WorkSummary workSummaryForExercise(list entries, Exercise exercise) { +WorkSummary workSummaryForExercise(GymLogEntryList entries, Exercise exercise) { WorkSummary result = {0}; UnixTimestamp lastTimestamp = 0; for (EachInReversed(entries, i)) { @@ -216,23 +220,23 @@ int gymTrackerLogWorkToday(Arena *arena, Exercise exercise) { statusCode = 1; } else { size_t entryCount = logfile.length / sizeof(GymLogEntry); - list logEntries = { (GymLogEntry *)logfile.str, entryCount, entryCount }; + GymLogEntryList logEntries = { (GymLogEntry *)logfile.str, entryCount, entryCount }; UnixTimestamp todayUnix = getSystemUnixTime(); Timestamp todayTs = timestampFromUnixTime(&todayUnix); - list todaysEntries = {0}; + GymLogEntryList todaysEntries = {0}; todaysEntries.data = logEntries.data; for (EachInReversed(logEntries, i)) { GymLogEntry logEntry = logEntries.data[i]; Timestamp logTs = timestampFromUnixTime(&logEntry.timestamp); if (logTs.tm_yday == todayTs.tm_yday && todayTs.tm_year == logTs.tm_year) { - todaysEntries.head += 1; + todaysEntries.length += 1; todaysEntries.data = &logEntries.data[i]; } } if (todaysEntries.data) { - todaysEntries.length = todaysEntries.head; + todaysEntries.capacity = todaysEntries.length; WorkSummary summary = workSummaryForExercise(todaysEntries, exercise); print("Total work today for %S:\n%.2fkg in ~%.2fmin.\n", exercise.name, summary.totalWork, (real32)summary.restTime / 60.0f); } @@ -241,7 +245,9 @@ int gymTrackerLogWorkToday(Arena *arena, Exercise exercise) { return statusCode; } -int32 gymTrackerStatus(Arena *arena, list args) { +DefineList(real32, Real32); +DefineList(uint32, Uint32); +int32 gymTrackerStatus(Arena *arena, StringList args) { int32 statusCode = 0; string file = os_readEntireFile(arena, LOG_FILE_LOCATION); @@ -253,9 +259,9 @@ int32 gymTrackerStatus(Arena *arena, list args) { Timestamp startTs = {0}; ParsePositiveIntResult numDays = {1, true}; - bool showAll = args.length == 1 && strEql(args.data[0], "--all"_s); + bool showAll = args.length == 1 && strEql(args.data[0], s("--all")); if (!showAll) { - if (args.length == 2 && (strEql(args.data[0], "--days"_s) || strEql(args.data[0], "-d"_s))) { + if (args.length == 2 && (strEql(args.data[0], s("--days")) || strEql(args.data[0], s("-d")))) { size_t l; numDays = parsePositiveInt(args.data[1], &l); } @@ -273,14 +279,14 @@ int32 gymTrackerStatus(Arena *arena, list args) { int lastDay = -1; int lastYear = -1; size_t entryCount = file.length / sizeof(GymLogEntry); - list logEntries = { (GymLogEntry *)file.str, entryCount, entryCount }; + GymLogEntryList logEntries = { (GymLogEntry *)file.str, entryCount, entryCount }; - list nameByExercise = PushFullListZero(arena, string, db->header.nextId); + StringList nameByExercise = PushFullListZero(arena, StringList, db->header.nextId); - list workPerExerciseByDay = PushFullListZero(arena, real32, db->header.nextId); - list workPerExerciseByPrevDay = PushFullListZero(arena, real32, db->header.nextId); - list restPerExerciseByDay = PushFullListZero(arena, uint32, db->header.nextId); - list lastTsPerExerciseByDay = PushFullListZero(arena, uint32, db->header.nextId); + Real32List workPerExerciseByDay = PushFullListZero(arena, Real32List, db->header.nextId); + Real32List workPerExerciseByPrevDay = PushFullListZero(arena, Real32List, db->header.nextId); + Uint32List restPerExerciseByDay = PushFullListZero(arena, Uint32List, db->header.nextId); + Uint32List lastTsPerExerciseByDay = PushFullListZero(arena, Uint32List, db->header.nextId); int dayCount = 0; @@ -336,21 +342,21 @@ int32 gymTrackerStatus(Arena *arena, list args) { if (prevEntry && entry->exerciseId == prevEntry->exerciseId) { nameToPrint = PushStringFill(arena, exerciseName->length, '.'); } else { - nameToPrint = exerciseName->str ? *exerciseName : "unknown-exercise"_s; + nameToPrint = exerciseName->str ? *exerciseName : s("unknown-exercise"); print("\n"); } - print(format, - formatTimeHms(arena, ×tamp), - nameToPrint, - entry->weightRepsInfo.weight, + print(format, + formatTimeHms(arena, ×tamp), + nameToPrint, + entry->weightRepsInfo.weight, entry->weightRepsInfo.reps); Timestamp nextTimestamp = {0}; - if (i < logEntries.head - 1) { + if (i < logEntries.length - 1) { nextTimestamp = timestampFromUnixTime(&logEntries.data[i + 1].timestamp); } - if (i == logEntries.head + 1 || nextTimestamp.tm_yday != lastDay || nextTimestamp.tm_year != lastYear) { + if (i == logEntries.length + 1 || nextTimestamp.tm_yday != lastDay || nextTimestamp.tm_year != lastYear) { print("\n"); print("Work summary:\n"); for (size_t j = 0; j < workPerExerciseByDay.length; j++) { @@ -377,9 +383,9 @@ int32 gymTrackerStatus(Arena *arena, list args) { workPerExerciseByPrevDay.data[j] = workToday; } } - zeroListFull(&workPerExerciseByDay); - zeroListFull(&restPerExerciseByDay); - zeroListFull(&lastTsPerExerciseByDay); + ZeroListFull(&workPerExerciseByDay); + ZeroListFull(&restPerExerciseByDay); + ZeroListFull(&lastTsPerExerciseByDay); prevEntry = 0; entry = 0; } @@ -390,7 +396,7 @@ int32 gymTrackerStatus(Arena *arena, list args) { return statusCode; } -int gymTrackerDeleteEntries(Arena *arena, list args) { +int gymTrackerDeleteEntries(Arena *arena, StringList args) { int statusCode = 0; if (args.length == 0) { @@ -400,7 +406,7 @@ int gymTrackerDeleteEntries(Arena *arena, list args) { size_t position = 0; ParsePositiveIntResult numToDeleteParsed = parsePositiveInt(args.data[0], &position); if (numToDeleteParsed.valid) { - list logEntries = loadEntryLog(arena, LOG_FILE_LOCATION); + GymLogEntryList logEntries = loadEntryLog(arena, LOG_FILE_LOCATION); if (numToDeleteParsed.result > logEntries.length) { print("%i is more than the current number of log entries (%i). Aborting.", numToDeleteParsed, logEntries.length); statusCode = 1; @@ -417,7 +423,7 @@ int gymTrackerDeleteEntries(Arena *arena, list args) { } // Syntax: do weightKg reps -int32 gymTrackerDo(Arena *arena, list args) { +int32 gymTrackerDo(Arena *arena, StringList args) { int32 statusCode = 0; Exercise exercise = {}; @@ -435,7 +441,7 @@ int32 gymTrackerDo(Arena *arena, list args) { for (EachIn(db->entries, i)) { GymLogDbParsedEntry entry = db->entries.data[i]; if (strStartsWith(entry.name, exercise.name)) { - existingEntry = &entry; + existingEntry = &entry; if (entry.name.length != exercise.name.length) { exercise.name = entry.name; print("Assuming exercise \"%S\".\n\n", entry.name); @@ -460,11 +466,11 @@ int32 gymTrackerDo(Arena *arena, list args) { statusCode = 1; } else { GymLogEntry entry = { - getSystemUnixTime(), - exercise.id, - { - reps.result, - kg.result, + .timestamp=getSystemUnixTime(), + .exerciseId=exercise.id, + .weightRepsInfo={ + .reps=reps.result, + .weight=kg.result, }, }; @@ -476,7 +482,7 @@ int32 gymTrackerDo(Arena *arena, list args) { return statusCode; } -int gymTrackerListExercises(Arena *arena, list args) { +int gymTrackerListExercises(Arena *arena, StringList args) { int statusCode = 0; GymLogDbParsed *db = parseDb(arena, os_readEntireFile(arena, DB_FILE_LOCATION)); @@ -492,7 +498,7 @@ int gymTrackerListExercises(Arena *arena, list args) { return statusCode; } -int gymTrackerAddExercise(Arena *arena, list args) { +int gymTrackerAddExercise(Arena *arena, StringList args) { int statusCode = 0; string newExerciseName = args.data[0]; @@ -557,21 +563,21 @@ int gymTrackerAddExercise(Arena *arena, list args) { return statusCode; } -int32 cmd_dispatch(Arena *arena, list args, list cmds) { +int32 cmd_dispatch(Arena *arena, StringList args, BasicCommandList cmds) { int32 result = 0; if (args.length < 1) { print("At least one arg is required.\n"); result = 1; - } else if (strEql(args.data[0], "--help"_s)) { + } else if (strEql(args.data[0], s("--help"))) { cmd_printHelp(arena, cmds, NULL); - } else if (args.length > 1 && strEql(args.data[1], "--help"_s)) { + } else if (args.length > 1 && strEql(args.data[1], s("--help"))) { cmd_printHelp(arena, cmds, &args.data[0]); } else { string userCmd = args.data[0]; for (EachIn(cmds, i)) { if (strEql(cmds.data[i].name, userCmd)) { - list argsRest = listSlice(args, 1); + StringList argsRest = ListTail(args, 1); cmds.data[i].command(arena, argsRest); } } @@ -583,89 +589,89 @@ int32 cmd_dispatch(Arena *arena, list args, list cmds) { int main(int argc, char **argv) { initialiseDjStdCore(); Arena *arena = arenaAlloc(Megabytes(64)); - list args = getArgs(arena, argc, argv); + StringList args = getArgs(arena, argc, argv); CmdOptionArg cmdStatusOptArgs[] = { { .charName = '\0', - .name = "all"_s, - .description = "Displays the full recorded history since day zero."_s, + .name = s("all"), + .description = s("Displays the full recorded history since day zero."), .type = CmdArgType_BOOL }, { .charName = 'd', - .name = "days"_s, - .description = "Displays the history for a previous number of days."_s, + .name = s("days"), + .description = s("Displays the history for a previous number of days."), .type = CmdArgType_INT, } }; BasicCommand cmdStatus = { - .name = "status"_s, - .description = "Shows the currently recorded exercises. Default displays the current day."_s, - .posArgs = EmptyList(CmdPositionalArg), - .optArgs = ArrayAsList(CmdOptionArg, cmdStatusOptArgs), + .name = s("status"), + .description = s("Shows the currently recorded exercises. Default displays the current day."), + .posArgs = EmptyList(CmdPositionalArgList), + .optArgs = ArrayAsList(CmdOptionArgList, cmdStatusOptArgs), .command = gymTrackerStatus, }; CmdPositionalArg cmdDoPosArgs[] = { { - .name = "exercise"_s, + .name = s("exercise"), .type = CmdArgType_STRING, }, { - .name = "weight"_s, - .description = "Weight moved for one repetition"_s, + .name = s("weight"), + .description = s("Weight moved for one repetition"), .type = CmdArgType_FLOAT, }, { - .name = "reps"_s, - .description = "Number of repetitions performed"_s, + .name = s("reps"), + .description = s("Number of repetitions performed"), .type = CmdArgType_INT, } }; BasicCommand cmdDo = { - .name = "do"_s, - .description = "Records an exercise with weight and reps"_s, - .posArgs = ArrayAsList(CmdPositionalArg, cmdDoPosArgs), - .optArgs = EmptyList(CmdOptionArg), + .name = s("do"), + .description = s("Records an exercise with weight and reps"), + .posArgs = ArrayAsList(CmdPositionalArgList, cmdDoPosArgs), + .optArgs = EmptyList(CmdOptionArgList), .command = gymTrackerDo, }; CmdPositionalArg cmdDeletePosArgs[] = { { - .name = "count"_s, - .description = "The number of entries to pop off the end of the record."_s, + .name = s("count"), + .description = s("The number of entries to pop off the end of the record."), .type = CmdArgType_INT, } }; BasicCommand cmdDelete = { - .name = "delete"_s, - .description = "Deletes the last given number of entries."_s, - .posArgs = ArrayAsList(CmdPositionalArg, cmdDeletePosArgs), - .optArgs = EmptyList(CmdOptionArg), + .name = s("delete"), + .description = s("Deletes the last given number of entries."), + .posArgs = ArrayAsList(CmdPositionalArgList, cmdDeletePosArgs), + .optArgs = EmptyList(CmdOptionArgList), .command = gymTrackerDeleteEntries, }; BasicCommand cmdList = { - .name = "list"_s, - .description = "Lists all available exercises in the database."_s, - .posArgs = EmptyList(CmdPositionalArg), - .optArgs = EmptyList(CmdOptionArg), + .name = s("list"), + .description = s("Lists all available exercises in the database."), + .posArgs = EmptyList(CmdPositionalArgList), + .optArgs = EmptyList(CmdOptionArgList), .command = gymTrackerListExercises, }; CmdPositionalArg cmdAddPosArgs[] = { { - .name = "name"_s, - .description = "The name of the exercise to be added."_s, + .name = s("name"), + .description = s("The name of the exercise to be added."), .type = CmdArgType_STRING, }, }; BasicCommand cmdAdd = { - .name = "add"_s, - .description = "Adds a new exercise name to the database."_s, - .posArgs = ArrayAsList(CmdPositionalArg, cmdAddPosArgs), - .optArgs = EmptyList(CmdOptionArg), + .name = s("add"), + .description = s("Adds a new exercise name to the database."), + .posArgs = ArrayAsList(CmdPositionalArgList, cmdAddPosArgs), + .optArgs = EmptyList(CmdOptionArgList), .command = gymTrackerAddExercise, }; @@ -677,7 +683,7 @@ int main(int argc, char **argv) { cmdAdd, }; - return cmd_dispatch(arena, args, ArrayAsList(BasicCommand, commands)); + return cmd_dispatch(arena, args, ArrayAsList(BasicCommandList, commands)); /* if (icmd.posArgs.length > 0) { @@ -695,23 +701,22 @@ int main(int argc, char **argv) { if (icmd.optArgs.length > 0) { print("\tOptions:\n"); } - */ int statusCode = 0; if (statusCode == 0) { string cmd = args.data[0]; - list argsRest = listSlice(args, 1); + list argsRest = ListSlice(args, 1); - if (strEql("status"_s, cmd)) { + if (strEql(s("status"), cmd)) { statusCode = gymTrackerStatus(arena, argsRest); - } else if (strEql("do"_s, cmd)) { + } else if (strEql(s("do"), cmd)) { statusCode = gymTrackerDo(arena, argsRest); - } else if (strEql("delete"_s, cmd)) { + } else if (strEql(s("delete"), cmd)) { statusCode = gymTrackerDeleteEntries(arena, argsRest); - } else if (strEql("list"_s, cmd)) { + } else if (strEql(s("list"), cmd)) { statusCode = gymTrackerListExercises(arena, argsRest); - } else if (strEql("add"_s, cmd)) { + } else if (strEql(s("add"), cmd)) { statusCode = gymTrackerAddExercise(arena, argsRest); } else { print("Unknown command \"%S\"\n", args.data[0]); @@ -720,4 +725,5 @@ int main(int argc, char **argv) { } return statusCode; + */ } diff --git a/build b/build index 876340a..52dcbc8 100755 --- a/build +++ b/build @@ -1,3 +1,3 @@ #!/bin/bash -g++ -Wall -g -g3 -DOS_LINUX ./app.cpp -o ./target/app +gcc -Wall -g -g3 -DOS_LINUX=1 -DENABLE_ASSERT=1 ./app.c -lm -o ./target/app diff --git a/djstdlib b/djstdlib index e9290ba..c137eb2 160000 --- a/djstdlib +++ b/djstdlib @@ -1 +1 @@ -Subproject commit e9290ba9f25419c39b345753e62dddfda4589d0f +Subproject commit c137eb24a753d80d8ca513d118f8cf6ce0b3962a