<commit_before>/**
 * @file
 *
 * @brief Source for yaml plugin
 *
 * @copyright BSD License (see doc/LICENSE.md or https://www.libelektra.org)
 *
 */

/* -- Imports --------------------------------------------------------------------------------------------------------------------------- */

#include "yaml.h"

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include <kdbassert.h>
#include <kdberrors.h>
#include <kdbhelper.h>
#include <kdblogger.h>

#ifdef HAVE_LOGGER
#include <libgen.h>
#include <sys/param.h>
#endif

/* -- Data Structures ------------------------------------------------------------------------------------------------------------------- */

/** This enum specifies the possible states of the recursive descent parser. */
typedef enum {
	/** Unable to open file */
	ERROR_FILE_OPEN,
	/** Unable to close file */
	ERROR_FILE_CLOSE,
	/** Error while parsing file */
	ERROR_PARSE,
	/** Everything is okay */
	OK
} statusType;

/** This structure saves various data for the recursive descent parser used in this plugin. */
typedef struct
{
	/** The current state of the parsing engine */
	statusType status;

	/** The handle of the opened file */
	FILE * file;
	/** Current line inside `file` */
	size_t line;
	/** Current column inside `line` */
	size_t column;
	/** Last read text consumed by parser */
	char * text;

	/** Text buffer for the content saved in `file` */
	char * bufferBase;
	/** Current location in the text buffer */
	char * buffer;
	/** Length of characters still available in the text buffer */
	size_t bufferCharsAvailable;

	/** Saves filename and allows us to emit error information */
	Key * parentKey;
	/** Contains key values pairs we parsed (get direction) or data we need to write back (set direction) */
	KeySet * keySet;

	/** Stores previous value of `errno` */
	int errorNumber;
} parserType;

/* -- Macros ---------------------------------------------------------------------------------------------------------------------------- */

#define LOG_PARSE(data, message, ...)                                                                                                      \
	ELEKTRA_LOG_DEBUG ("%s:%lu:%lu: " message, strrchr (keyString (data->parentKey), '/') + 1, data->line, data->column, __VA_ARGS__);

#define SET_ERROR_PARSE(data, message, ...)                                                                                                \
	ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_PARSE, data->parentKey, "%s:%lu:%lu: " message, keyString (data->parentKey), data->line,         \
			    data->column, __VA_ARGS__);
#define RET_NOK(function)                                                                                                                  \
	if (function->status != OK)                                                                                                        \
	{                                                                                                                                  \
		return parser; /* Requires that the name of the parsing structure is `parser`! */                                          \
	}

#define ASSERT_NOT_NULL(argument) ELEKTRA_ASSERT (argument, "The variable `" #argument "` contains `NULL`.")

/* -- Functions ------------------------------------------------------------------------------------------------------------------------- */

// ========
// = Misc =
// ========

static parserType * setError (parserType * const parser, statusType status)
{
	ASSERT_NOT_NULL (parser);

	SET_ERROR_PARSE (parser, "%s", strerror (errno));
	errno = parser->errorNumber;
	parser->status = status;
	return parser;
}

// ===========
// = Parsing =
// ===========

static parserType * assertNumberCharsAvailable (parserType * const parser, size_t numberChars)
{
	ASSERT_NOT_NULL (parser);

	char * line = NULL;
	size_t capacity;
	ssize_t numberCharsRead;

	while (parser->bufferCharsAvailable < numberChars && (numberCharsRead = getline (&line, &capacity, parser->file)) != -1)
	{
		size_t bufferCharsAvailable = parser->bufferCharsAvailable + numberCharsRead;
		char * newBuffer = elektraMalloc (bufferCharsAvailable + 1);

		if (!newBuffer) return setError (parser, ERROR_PARSE);
		strncpy (newBuffer, parser->buffer, parser->bufferCharsAvailable);
		strncpy (newBuffer + parser->bufferCharsAvailable, line, bufferCharsAvailable + 1);

		elektraFree (parser->bufferBase);
		free (line);

		parser->bufferBase = parser->buffer = newBuffer;
		parser->bufferCharsAvailable = bufferCharsAvailable;
	}

	if (feof (parser->file) && parser->bufferCharsAvailable < numberChars) return setError (parser, ERROR_PARSE);
	return parser;
}

static parserType * getNextChar (parserType * parser)
{
	ASSERT_NOT_NULL (parser);

	RET_NOK (assertNumberCharsAvailable (parser, 1));

	parser->bufferCharsAvailable--;
	parser->text = parser->buffer;
	parser->buffer++;

	return parser;
}

static parserType * putBackChars (parserType * parser, size_t numberChars)
{
	ASSERT_NOT_NULL (parser);

	parser->bufferCharsAvailable += numberChars;
	parser->buffer -= numberChars;

	return parser;
}

static bool acceptChars (parserType * const parser, char const * const characters, size_t numberCharacters)
{
	ASSERT_NOT_NULL (parser);
	ASSERT_NOT_NULL (parser->file);
	ASSERT_NOT_NULL (characters);

	RET_NOK (getNextChar (parser));

	for (size_t charIndex = 0; charIndex < numberCharacters; charIndex++)
	{
		if (*parser->text == characters[charIndex])
		{<commit_msg>YAML: Add assertion<commit_after>/**
 * @file
 *
 * @brief Source for yaml plugin
 *
 * @copyright BSD License (see doc/LICENSE.md or https://www.libelektra.org)
 *
 */

/* -- Imports --------------------------------------------------------------------------------------------------------------------------- */

#include "yaml.h"

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include <kdbassert.h>
#include <kdberrors.h>
#include <kdbhelper.h>
#include <kdblogger.h>

#ifdef HAVE_LOGGER
#include <libgen.h>
#include <sys/param.h>
#endif

/* -- Data Structures ------------------------------------------------------------------------------------------------------------------- */

/** This enum specifies the possible states of the recursive descent parser. */
typedef enum {
	/** Unable to open file */
	ERROR_FILE_OPEN,
	/** Unable to close file */
	ERROR_FILE_CLOSE,
	/** Error while parsing file */
	ERROR_PARSE,
	/** Everything is okay */
	OK
} statusType;

/** This structure saves various data for the recursive descent parser used in this plugin. */
typedef struct
{
	/** The current state of the parsing engine */
	statusType status;

	/** The handle of the opened file */
	FILE * file;
	/** Current line inside `file` */
	size_t line;
	/** Current column inside `line` */
	size_t column;
	/** Last read text consumed by parser */
	char * text;

	/** Text buffer for the content saved in `file` */
	char * bufferBase;
	/** Current location in the text buffer */
	char * buffer;
	/** Length of characters still available in the text buffer */
	size_t bufferCharsAvailable;

	/** Saves filename and allows us to emit error information */
	Key * parentKey;
	/** Contains key values pairs we parsed (get direction) or data we need to write back (set direction) */
	KeySet * keySet;

	/** Stores previous value of `errno` */
	int errorNumber;
} parserType;

/* -- Macros ---------------------------------------------------------------------------------------------------------------------------- */

#define LOG_PARSE(data, message, ...)                                                                                                      \
	ELEKTRA_LOG_DEBUG ("%s:%lu:%lu: " message, strrchr (keyString (data->parentKey), '/') + 1, data->line, data->column, __VA_ARGS__);

#define SET_ERROR_PARSE(data, message, ...)                                                                                                \
	ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_PARSE, data->parentKey, "%s:%lu:%lu: " message, keyString (data->parentKey), data->line,         \
			    data->column, __VA_ARGS__);
#define RET_NOK(function)                                                                                                                  \
	if (function->status != OK)                                                                                                        \
	{                                                                                                                                  \
		return parser; /* Requires that the name of the parsing structure is `parser`! */                                          \
	}

#define ASSERT_NOT_NULL(argument) ELEKTRA_ASSERT (argument, "The variable `" #argument "` contains `NULL`.")

/* -- Functions ------------------------------------------------------------------------------------------------------------------------- */

// ========
// = Misc =
// ========

static parserType * setError (parserType * const parser, statusType status)
{
	ASSERT_NOT_NULL (parser);

	SET_ERROR_PARSE (parser, "%s", strerror (errno));
	errno = parser->errorNumber;
	parser->status = status;
	return parser;
}

// ===========
// = Parsing =
// ===========

static parserType * assertNumberCharsAvailable (parserType * const parser, size_t numberChars)
{
	ASSERT_NOT_NULL (parser);

	char * line = NULL;
	size_t capacity;
	ssize_t numberCharsRead;

	while (parser->bufferCharsAvailable < numberChars && (numberCharsRead = getline (&line, &capacity, parser->file)) != -1)
	{
		size_t bufferCharsAvailable = parser->bufferCharsAvailable + numberCharsRead;
		char * newBuffer = elektraMalloc (bufferCharsAvailable + 1);

		if (!newBuffer) return setError (parser, ERROR_PARSE);
		strncpy (newBuffer, parser->buffer, parser->bufferCharsAvailable);
		strncpy (newBuffer + parser->bufferCharsAvailable, line, bufferCharsAvailable + 1);

		elektraFree (parser->bufferBase);
		free (line);

		parser->bufferBase = parser->buffer = newBuffer;
		parser->bufferCharsAvailable = bufferCharsAvailable;
	}

	if (feof (parser->file) && parser->bufferCharsAvailable < numberChars) return setError (parser, ERROR_PARSE);
	return parser;
}

static parserType * getNextChar (parserType * parser)
{
	ASSERT_NOT_NULL (parser);

	RET_NOK (assertNumberCharsAvailable (parser, 1));

	parser->bufferCharsAvailable--;
	parser->text = parser->buffer;
	parser->buffer++;

	return parser;
}

static parserType * putBackChars (parserType * parser, size_t numberChars)
{
	ASSERT_NOT_NULL (parser);
	ELEKTRA_ASSERT (parser->buffer - numberChars >= parser->bufferBase, "Can not put back more characters than available.");

	parser->bufferCharsAvailable += numberChars;
	parser->buffer -= numberChars;

	return parser;
}

static bool acceptChars (parserType * const parser, char const * const characters, size_t numberCharacters)
{
	ASSERT_NOT_NULL (parser);
	ASSERT_NOT_NULL (parser->file);
	ASSERT_NOT_NULL (characters);

	RET_NOK (getNextChar (parser));

	for (size_t charIndex = 0; charIndex < numberCharacters; charIndex++)
	{
		if (*parser->text == characters[charIndex])
		{
{'fraction_non_alphanumeric': 0.2005939524838013, 'fraction_numerical': 0.0010799136069114472, 'mean_word_length': 1.9015665796344647, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 86, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 50, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4085663', 'n_tokens_mistral': 3001, 'n_tokens_neox': 2818, 'n_words': 826}