From b9c9b216125c0044842b0b145cc26d3c88d78297 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Tue, 18 Nov 2025 13:58:08 +0100 Subject: [PATCH] feat: strings and config modules --- .clang-format | 79 +++++++++++++++ httpd/.gitignore | 7 ++ httpd/Makefile | 31 ++++++ httpd/config.txt | 9 ++ httpd/config_reader.sh | 86 ++++++++++++++++ httpd/src/config/config.c | 170 ++++++++++++++++++++++++++++++++ httpd/src/config/config.h | 73 ++++++++++++++ httpd/src/main.c | 4 + httpd/src/utils/string/string.c | 70 +++++++++++++ httpd/src/utils/string/string.h | 47 +++++++++ 10 files changed, 576 insertions(+) create mode 100644 .clang-format create mode 100644 httpd/.gitignore create mode 100644 httpd/Makefile create mode 100644 httpd/config.txt create mode 100755 httpd/config_reader.sh create mode 100644 httpd/src/config/config.c create mode 100644 httpd/src/config/config.h create mode 100644 httpd/src/main.c create mode 100644 httpd/src/utils/string/string.c create mode 100644 httpd/src/utils/string/string.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..7ed8115 --- /dev/null +++ b/.clang-format @@ -0,0 +1,79 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterEnum: true + AfterClass: true + AfterControlStatement: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +FixNamespaceComments: true +ForEachMacros: ['ILIST_FOREACH', 'ILIST_FOREACH_ENTRY'] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '<.*>' + Priority: 1 + - Regex: '.*' + Priority: 2 +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +NamespaceIndentation: All +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/httpd/.gitignore b/httpd/.gitignore new file mode 100644 index 0000000..3b1f71d --- /dev/null +++ b/httpd/.gitignore @@ -0,0 +1,7 @@ +*~ +*.swp +*.o +*.a +*.so +*.log +*.core diff --git a/httpd/Makefile b/httpd/Makefile new file mode 100644 index 0000000..cf0ef94 --- /dev/null +++ b/httpd/Makefile @@ -0,0 +1,31 @@ +CC = gcc +CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla -pedantic +LDFLAGS = +LDLIBS = + +LDFLAGS_DBG = -g +ASAN_DBG_FLAGS = -fsanitize=address + +UTILS_SRCS = src/utils/string/string.c +CONFIG_SRCS = src/config/config.c +SRCS = $(UTILS_SRCS) $(CONFIG_SRCS) src/main.c + +UTILS_OBJS = ${UTILS_SRCS:.c=.o} +CONFIG_OBJS = ${CONFIG_SRCS:.c=.o} +OBJS = ${SRCS:.c=.o} + +TARGET=httpd + +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + +check: + dash tests/run.sh + +debug: LDFLAGS += LDFLAGS_DBG +debug: + $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS) + +clean: + $(RM) $(TARGET) + $(RM) $(OBJS) diff --git a/httpd/config.txt b/httpd/config.txt new file mode 100644 index 0000000..c35b687 --- /dev/null +++ b/httpd/config.txt @@ -0,0 +1,9 @@ +[global] +log = true +pid_file = /tmp/HTTPd.pid + +[[vhosts]] +server_name = my_server +ip = 127.0.0.1 +port = 6996 +root_dir = . diff --git a/httpd/config_reader.sh b/httpd/config_reader.sh new file mode 100755 index 0000000..a4242cc --- /dev/null +++ b/httpd/config_reader.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +usage() { + echo "Usage: $0 --path-config [--path-bin ] [--daemon ]" + echo + echo "Options:" + echo " --path-config (mandatory) path to the config file" + echo " --path-bin (optional) path to the httpd binary, default is ./httpd" + echo " --daemon (optional) option passed to httpd" + echo " -h, --help prints this help page" + exit 1 +} + +OPTIONS=$(getopt -o h --long path-config:,path-bin:,daemon:,help -- "$@") +if [ $? -ne 0 ]; then + usage +fi + +eval set -- "$OPTIONS" + +PATH_CONFIG="" +PATH_BIN="./httpd" +DAEMON_OPTS="" + +# Lecture des arguments +while true; do + case "$1" in + --path-config) + PATH_CONFIG="$2" + shift 2 + ;; + --path-bin) + PATH_BIN="$2" + shift 2 + ;; + --daemon) + DAEMON_OPTS="--daemon $2" + shift 2 + ;; + -h|--help) + usage + ;; + --) + shift + break + ;; + *) + usage + ;; + esac +done + +# verify mandatory option +if [ -z "$PATH_CONFIG" ]; then + echo "error : --path-config is mandatory" + echo + usage +fi + +! [ -f "$PATH_CONFIG" ] && (echo "config file: $PATH_CONFIG is not a file"; exit 1;) +PATH_BIN_TO_RUN="$PATH_BIN" + +if [[ "$PATH_BIN_TO_RUN" != */* ]]; then + PATH_BIN_TO_RUN="./$PATH_BIN_TO_RUN" +fi + +[ -x "$PATH_BIN_TO_RUN" ] || (echo "error: binary file: '$PATH_BIN_TO_RUN' is not executable or does not exist."; exit 1;) + +PATH_BIN="$PATH_BIN_TO_RUN" + +# main +ARGS="" +regex="([^ ]+) ?= ?([^ ]+)" + +while IFS= read -r line || [[ -n "$line" ]]; +do + [ "$line" = "[global]" ] || [ "$line" = "[[vhosts]]" ] && continue; + + if [[ $line =~ $regex ]]; then + ARGS="$ARGS --${BASH_REMATCH[1]} ${BASH_REMATCH[2]}" + fi +done < "$PATH_CONFIG" + +# launch binary with correct args +echo "command run is: $PATH_BIN $DAEMON_OPTS ${ARGS:1}" +$PATH_BIN $DAEMON_OPTS ${ARGS:1} diff --git a/httpd/src/config/config.c b/httpd/src/config/config.c new file mode 100644 index 0000000..274ed30 --- /dev/null +++ b/httpd/src/config/config.c @@ -0,0 +1,170 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "../utils/string/string.h" + +#define ARG_VALID 0 +#define ARG_INVALID 1 +#define ARG_HELP 2 +#define ARG_NOT_IMPLEMENTED 2 + +// == Static functions + +// Returns 0 if the servers array is valid 1 otherwise +// static int check_server_config(struct server_config *server) +// { +// if (server->server_name == NULL || server->port == NULL +// || server->ip == NULL || server->root_dir == NULL) +// { +// return -1; +// } + +// return 0; +// } + +static void print_help(char *program_name) +{ + printf("Usage: %s [OPTION]\n", program_name); + puts(""); +} + +// Returns 0 if argument is valid, otherwise +static int handle_opt(char **argv, char opt, struct config *cfg) +{ + // Options without value + switch (opt) + { + // Help + case 'h': + print_help(argv[0]); + return ARG_HELP; + } + + if (optarg == NULL) + return ARG_INVALID; + + // Options with value + switch (opt) + { + // Daemon + case 'd': + if (strcmp(optarg, "start") == 0) + cfg->daemon = START; + else if (strcmp(optarg, "stop") == 0) + cfg->daemon = STOP; + else if (strcmp(optarg, "restart") == 0) + cfg->daemon = RESTART; + else + return ARG_INVALID; + return ARG_VALID; + + // Server name + case 's': + cfg->servers->server_name = string_create(optarg, strlen(optarg)); + break; + + // Port + case 'p': + cfg->servers->port = optarg; + break; + + // IP + case 'i': + cfg->servers->ip = optarg; + break; + + // Root dir + case 'r': + cfg->servers->root_dir = optarg; + break; + + // Default file + case 'D': + cfg->servers->ip = optarg; + break; + + // PID file + case 'P': + cfg->pid_file = optarg; + break; + + // Log file + case 'L': + cfg->log_file = optarg; + break; + + // Logs + case 'l': + return ARG_NOT_IMPLEMENTED; + + default: + // TODO print full flags + // printf("Unrecognized option '%s'", optstring); + return ARG_INVALID; + } + + return ARG_VALID; +} + +// == Main functions + +struct config *parse_configuration(int argc, char *argv[]) +{ + struct option options[] = { // Global + { "daemon", optional_argument, NULL, 'd' }, + { "help", optional_argument, NULL, 'h' }, + // Vhosts + { "server_name", required_argument, NULL, 's' }, + { "port", required_argument, NULL, 'p' }, + { "ip", required_argument, NULL, 'i' }, + { "root_dir", required_argument, NULL, 'r' }, + { "defaut_file", optional_argument, NULL, 'D' }, + // Logging + { "pid_file", required_argument, NULL, 'P' }, + { "log_file", optional_argument, NULL, 'L' }, + { "log", optional_argument, NULL, 'l' }, + // End + { NULL, 0, NULL, 0 } + }; + + struct config *config = calloc(1, sizeof(struct config)); + if (config == NULL) + return NULL; + + config->servers = calloc(1, sizeof(struct server_config)); + if (config->servers == NULL) + { + free(config); + return NULL; + } + + char opt; + int optindex; + while ((opt = getopt_long(argc, argv, "*", options, &optindex)) != -1) + { + int err = handle_opt(argv, opt, config); + if (err != ARG_VALID) + { + if (err == ARG_INVALID) + printf("%s: Invalid argument '%s'", argv[0], argv[optindex]); + if (err == ARG_NOT_IMPLEMENTED) + printf("%s: This function is not implemented", argv[0]); + + free(config->servers); + free(config); + return NULL; + } + } + + return config; +} + +void config_destroy(struct config *config) +{ + free(config->servers); + free(config); +} diff --git a/httpd/src/config/config.h b/httpd/src/config/config.h new file mode 100644 index 0000000..aa4c318 --- /dev/null +++ b/httpd/src/config/config.h @@ -0,0 +1,73 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define _XOPEN_SOURCE 500 + +#include + +/* +** @brief Enum daemon +** NO_OPTION if the '--daemon' option is not given +** START, STOP, RESTART if option is "start", "stop" and "restart" +*/ +enum daemon +{ + NO_OPTION = 0, + START, + STOP, + RESTART +}; + +/* +** @brief Configuration structure +** +** @param pid_file Path to the pid file +** @param log_file Path to the log file +** @param log Enable or disable logging +** @param servers Array of vhosts +** @daemon option for the daemon (START, STOP, RESTART) +*/ +struct config +{ + char *pid_file; + char *log_file; + bool log; + + struct server_config *servers; + enum daemon daemon; +}; + +/* +** @brief Vhost configuration structure +** +** @param server_name Server name +** @param port Port to listen on +** @param ip IP address +** @param root_dir Root directory to serve +** @param default_file Default file to serve +*/ +struct server_config +{ + struct string *server_name; + char *port; + char *ip; + char *root_dir; + char *default_file; +}; + +/* +** @brief Parse the given argv argument and returns a filled struct +** +** @param argc Number of arguments +** @param argv A list of char * containing all the arguments +*/ +struct config *parse_configuration(int argc, char *argv[]); + +/* +** @brief Free the config struct +** +** @param config The config struct to free +*/ +void config_destroy(struct config *config); + +#endif /* !CONFIG_H */ diff --git a/httpd/src/main.c b/httpd/src/main.c new file mode 100644 index 0000000..58fe692 --- /dev/null +++ b/httpd/src/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/httpd/src/utils/string/string.c b/httpd/src/utils/string/string.c new file mode 100644 index 0000000..342d7b2 --- /dev/null +++ b/httpd/src/utils/string/string.c @@ -0,0 +1,70 @@ +#include "string.h" + +#include +#include + +struct string *string_create(const char *str, size_t size) +{ + struct string *res = malloc(sizeof(struct string)); + if (res == NULL) + return NULL; + + res->data = malloc(size * sizeof(char)); + if (res->data == NULL) + { + free(res); + return NULL; + } + + memcpy(res->data, str, size); + res->size = size; + return res; +} + +int string_compare_n_str(const struct string *str1, const char *str2, size_t n) +{ + size_t i = 0; + int res = 0; + char reached_str2_end = 0; + while (i < n) + { + if (i < str1->size) + res += str1->data[i]; + + if (!reached_str2_end) + { + if (str2[i] == '\0') + reached_str2_end = 1; + else + res -= str2[i]; + } + + i++; + } + + return res; +} + +void string_concat_str(struct string *str, const char *to_concat, size_t size) +{ + size_t new_size = str->size + size; + size_t str_size = str->size; + + if (new_size == 0) + return; + + str->data = realloc(str->data, new_size); + if (str->data == NULL) + return; // Handle ? + + for (size_t i = 0; i < size; i++) + { + str->data[str_size + i] = to_concat[i]; + } +} + +void string_destroy(struct string *str) +{ + free(str->data); + free(str); +} diff --git a/httpd/src/utils/string/string.h b/httpd/src/utils/string/string.h new file mode 100644 index 0000000..afe6d9d --- /dev/null +++ b/httpd/src/utils/string/string.h @@ -0,0 +1,47 @@ +#ifndef STRING_H +#define STRING_H + +#include +#include + +struct string +{ + size_t size; + char *data; +}; + +/* + ** @brief Create new string struct from char * and size + ** Be careful, the argument str will not be deallocated and thus you + ** can call this function with a static char * + ** + ** @param str + ** @param size + ** + ** @return the newly allocated string + */ +struct string *string_create(const char *str, size_t size); + +/* + ** @brief Act like memcmp(3) but for struct string and char * + ** + ** @param str1 + ** @param str2 + ** @param n + ** + ** @return + */ +int string_compare_n_str(const struct string *str1, const char *str2, size_t n); + +/* + ** @brief Concat a char * with its size in a struct string + ** + ** @param str + ** @param to_concat + ** @param size + */ +void string_concat_str(struct string *str, const char *to_concat, size_t size); + +void string_destroy(struct string *str); + +#endif /* ! STRING_H */