feat: strings and config modules

This commit is contained in:
Gu://em_ 2025-11-18 13:58:08 +01:00
commit b9c9b21612
10 changed files with 576 additions and 0 deletions

79
.clang-format Normal file
View file

@ -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

7
httpd/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
*~
*.swp
*.o
*.a
*.so
*.log
*.core

31
httpd/Makefile Normal file
View file

@ -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)

9
httpd/config.txt Normal file
View file

@ -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 = .

86
httpd/config_reader.sh Executable file
View file

@ -0,0 +1,86 @@
#!/bin/sh
usage() {
echo "Usage: $0 --path-config <file> [--path-bin <bin>] [--daemon <options>]"
echo
echo "Options:"
echo " --path-config <fichier> (mandatory) path to the config file"
echo " --path-bin <chemin-binaire> (optional) path to the httpd binary, default is ./httpd"
echo " --daemon <options> (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}

170
httpd/src/config/config.c Normal file
View file

@ -0,0 +1,170 @@
#include "config.h"
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}

73
httpd/src/config/config.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef CONFIG_H
#define CONFIG_H
#define _XOPEN_SOURCE 500
#include <stdbool.h>
/*
** @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 */

4
httpd/src/main.c Normal file
View file

@ -0,0 +1,4 @@
int main(void)
{
return 0;
}

View file

@ -0,0 +1,70 @@
#include "string.h"
#include <stdlib.h>
#include <string.h>
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);
}

View file

@ -0,0 +1,47 @@
#ifndef STRING_H
#define STRING_H
#include <ctype.h>
#include <stddef.h>
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 */