make json output smaller
This commit is contained in:
parent
1ff7adac56
commit
7664c7d2c3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
output
|
build
|
||||||
|
28
Makefile
28
Makefile
@ -1,22 +1,20 @@
|
|||||||
.DEFAULT_GOAL := run
|
BUILD_DIR := build
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
.PHONY := raylib_example_indexer all clean
|
.PHONY := raylib_example_indexer all clean
|
||||||
|
|
||||||
raylib_example_indexer: src/main.c
|
example_indexer: src/main.c
|
||||||
mkdir -p output
|
mkdir -p $(BUILD_DIR)
|
||||||
gcc src/main.c -o output/raylib_example_indexer -I./external -DSTB_C_LEXER_IMPLEMENTATION
|
gcc src/main.c -o $(BUILD_DIR)/example_indexer -I./external -DSTB_C_LEXER_IMPLEMENTATION
|
||||||
|
|
||||||
raylib_api:
|
$(BUILD_DIR)/raylib:
|
||||||
mkdir -p output
|
mkdir -p $(BUILD_DIR)
|
||||||
curl https://raw.githubusercontent.com/raysan5/raylib/master/parser/output/raylib_api.txt -o output/raylib_api.txt
|
git clone git@github.com:raysan5/raylib.git $(BUILD_DIR)/raylib
|
||||||
|
|
||||||
raylib:
|
run: example_indexer
|
||||||
mkdir -p output
|
./$(BUILD_DIR)/example_indexer $(BUILD_DIR)/raylib/src $(BUILD_DIR)/raylib/examples $(BUILD_DIR)/output.json
|
||||||
git clone git@github.com:raysan5/raylib.git output/raylib
|
|
||||||
|
|
||||||
run: raylib_example_indexer
|
all: $(BUILD_DIR)/raylib example_indexer run
|
||||||
./output/raylib_example_indexer output/raylib_api.txt output/raylib/examples output/output.json
|
|
||||||
|
|
||||||
all: raylib_api raylib run
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf output
|
rm -rf $(BUILD_DIR)
|
||||||
|
520
src/main.c
520
src/main.c
@ -9,311 +9,297 @@
|
|||||||
|
|
||||||
#include "stb_c_lexer.h"
|
#include "stb_c_lexer.h"
|
||||||
|
|
||||||
#define MAX_FUNCS_TO_PARSE 1024 // Maximum number of functions to parse
|
#include "raylib_parser.c"
|
||||||
#define MAX_FUNC_USAGES 1024 // Maximum number of usages per function
|
|
||||||
|
|
||||||
typedef struct function_info {
|
#define MAX_FUNCS_TO_PARSE 1024 // Maximum number of functions to parse
|
||||||
char name[64];
|
#define MAX_FUNCS_PER_EXAMPLE 1024 // Maximum number of usages per function per file
|
||||||
int name_size;
|
|
||||||
int param_count;
|
|
||||||
} function_info;
|
|
||||||
|
|
||||||
typedef struct function_usage {
|
typedef struct {
|
||||||
char filename[PATH_MAX];
|
char filename[256];
|
||||||
int line_number;
|
// TODO: Track where function usage was found and display it?
|
||||||
int line_offset;
|
} FunctionUsage;
|
||||||
} function_usage;
|
|
||||||
|
|
||||||
typedef struct line_range {
|
typedef struct {
|
||||||
int from, to; // [from, to) - from inclusive, to exclusive
|
int from, to; // [from, to) - from inclusive, to exclusive
|
||||||
} line_range;
|
} LineRange;
|
||||||
|
|
||||||
static int get_file_size(FILE *file) {
|
static bool StartsWith(char *text, int textSize, char *prefix, int prefixSize)
|
||||||
fseek(file, 0, SEEK_END);
|
{
|
||||||
int size = ftell(file);
|
return textSize >= prefixSize && !strncmp(text, prefix, prefixSize);
|
||||||
fseek(file, 0, SEEK_SET);
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_next_line(line_range *line, char *text, int text_size, int from) {
|
static bool EndsSith(char *text, int textSize, char *suffix, int suffixSize)
|
||||||
for (int i = from; i < text_size; i++) {
|
{
|
||||||
if (text[i] == '\n') {
|
return textSize >= suffixSize && !strncmp(text+textSize-suffixSize, suffix, suffixSize);
|
||||||
line->from = from;
|
|
||||||
line->to = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int skip_next_lines(char *text, int text_size, int line_count, int from) {
|
static bool GetNextLine(LineRange *line, char *text, int textSize, int from)
|
||||||
int next_line_from = from;
|
{
|
||||||
line_range curr = { 0 };
|
for (int i = from; i < textSize; i++) {
|
||||||
|
if (text[i] == '\n') {
|
||||||
for (int i = 0; i < line_count; i++) {
|
line->from = from;
|
||||||
if (!get_next_line(&curr, text, text_size, next_line_from)) break;
|
line->to = i;
|
||||||
next_line_from = curr.to+1;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return next_line_from;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_start_of_function_block(char *raylib_api, int raylib_api_size) {
|
static int GetFunctionFromIdentifier(char *id, FunctionInfo *functions, int functionCount)
|
||||||
int next_line_from = 0;
|
{
|
||||||
line_range curr = { 0 };
|
int idSize = strlen(id);
|
||||||
while (get_next_line(&curr, raylib_api, raylib_api_size, next_line_from)) {
|
|
||||||
int line_size = curr.to - curr.from;
|
|
||||||
char *line = &raylib_api[curr.from];
|
|
||||||
|
|
||||||
if (line_size >= sizeof("Functions found:") && !strncmp(line, "Functions found:", sizeof("Functions found:")-1)) {
|
for (int i = 0; i < functionCount; i++) {
|
||||||
line_range next_line;
|
FunctionInfo *function = &functions[i];
|
||||||
if (get_next_line(&next_line, raylib_api, raylib_api_size, curr.to+1)) {
|
if (idSize > sizeof(function->name)) continue;
|
||||||
return next_line.to+1;
|
if (!strcmp(id, function->name)) {
|
||||||
} else {
|
return i;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
}
|
return -1;
|
||||||
|
|
||||||
next_line_from = curr.to+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool parse_function_info(char *line, int line_size, function_info *info) {
|
static bool ParseFunctionUsagesFromFile(char *directory, char *filePath, FunctionUsage *usages[], int *usageCounts, FunctionInfo *functions, int functionCount)
|
||||||
char *name = strchr(line, ':') + 2;
|
{
|
||||||
int name_size = strchr(line, '(') - name;
|
char fullPath[PATH_MAX] = { 0 };
|
||||||
strncpy(info->name, name, name_size);
|
snprintf(fullPath, sizeof(fullPath), "%s/%s", directory, filePath);
|
||||||
info->name_size = name_size;
|
|
||||||
|
|
||||||
int param_count = strtoul(name + name_size + 4, NULL, 10);
|
int fileSize = 0;
|
||||||
info->param_count = param_count;
|
char *exampleCode = LoadFileText(fullPath, &fileSize);
|
||||||
|
if (exampleCode == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
stb_lexer lexer;
|
||||||
|
char stringStore[512];
|
||||||
|
stb_c_lexer_init(&lexer, exampleCode, exampleCode+fileSize, stringStore, sizeof(stringStore));
|
||||||
|
|
||||||
|
while (stb_c_lexer_get_token(&lexer)) {
|
||||||
|
if (lexer.token != CLEX_id) continue;
|
||||||
|
|
||||||
|
int functionIndex = GetFunctionFromIdentifier(lexer.string, functions, functionCount);
|
||||||
|
if (functionIndex != -1) {
|
||||||
|
int *usageCount = &usageCounts[functionIndex];
|
||||||
|
assert(*usageCount < MAX_FUNCS_PER_EXAMPLE);
|
||||||
|
FunctionUsage *usage = &usages[functionIndex][*usageCount];
|
||||||
|
strncpy(usage->filename, filePath, strlen(filePath));
|
||||||
|
(*usageCount)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(exampleCode);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_funcs_from_raylib_api(char *raylib_api, int raylib_api_size, function_info *funcs, int max_funcs) {
|
static void ParseFunctionsUsagesFromFolder(char *cwd, char *dir, FunctionUsage *usages[], int *usageCounts, FunctionInfo *functions, int functionCount)
|
||||||
int start_of_functions = find_start_of_function_block(raylib_api, raylib_api_size);
|
{
|
||||||
if (start_of_functions == -1) {
|
char dirPath[PATH_MAX];
|
||||||
return -1;
|
snprintf(dirPath, sizeof(dirPath), "%s/%s", cwd, dir);
|
||||||
}
|
DIR *dirp = opendir(dirPath);
|
||||||
|
if (dirp == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open directory '%s'\n", dirPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int count = 0;
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dirp)) != NULL) {
|
||||||
|
if (entry->d_type != DT_REG) continue;
|
||||||
|
|
||||||
int next_line_from = start_of_functions;
|
char *extension = strrchr(entry->d_name, '.');
|
||||||
line_range curr = { 0 };
|
if (!strcmp(extension, ".c")) {
|
||||||
while (get_next_line(&curr, raylib_api, raylib_api_size, next_line_from)) {
|
char filePath[PATH_MAX];
|
||||||
int line_size = curr.to - curr.from;
|
snprintf(filePath, sizeof(filePath), "%s/%s", dir, entry->d_name);
|
||||||
char *line = &raylib_api[curr.from];
|
ParseFunctionUsagesFromFile(cwd, filePath, usages, usageCounts, functions, functionCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function_info *func_info = &funcs[count];
|
closedir(dirp);
|
||||||
if (!parse_function_info(line, line_size, func_info)) {
|
|
||||||
fprintf(stderr, "Failed to parse function line: %.*s\n", line_size, line);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
|
|
||||||
int skip_count = 3 + MAX(func_info->param_count, 1);
|
|
||||||
next_line_from = skip_next_lines(raylib_api, raylib_api_size, skip_count, curr.to+1);
|
|
||||||
|
|
||||||
if (max_funcs == count) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_func_from_identifier(char *id, function_info *funcs, int func_count) {
|
// Checks if the line is in the format "#if defined(*_IMPLEMENTATION)"
|
||||||
int id_size = strlen(id);
|
static bool IsLineImplementationIfdef(char *line, int line_size) {
|
||||||
for (int i = 0; i < func_count; i++) {
|
char *prefix = "#if defined(";
|
||||||
function_info *func = &funcs[i];
|
char *suffix = "_IMPLEMENTATION)";
|
||||||
if (id_size != func->name_size) continue;
|
return StartsWith(line, line_size, prefix, strlen(prefix)) &&
|
||||||
if (!strncmp(id, func->name, func->name_size)) {
|
EndsSith(line, line_size, suffix, strlen(suffix));
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool collect_function_usages_from_file(char *directory, char *file_path, function_usage *usages[], int *usage_counts, function_info *funcs, int func_count) {
|
static int ParseFunctionsDefinitionsFromHeader(char *path, FunctionInfo *functions, int maxFunctions)
|
||||||
char full_path[PATH_MAX] = { 0 };
|
{
|
||||||
snprintf(full_path, sizeof(full_path), "%s/%s", directory, file_path);
|
int fileSize;
|
||||||
FILE *file = fopen(full_path, "r");
|
char *contents = LoadFileText(path, &fileSize);
|
||||||
if (file == NULL) {
|
|
||||||
fprintf(stderr, "Failed to open file '%s'\n", full_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_size = get_file_size(file);
|
int count = 0;
|
||||||
char *example_code = malloc(file_size);
|
|
||||||
fread(example_code, sizeof(char), file_size, file);
|
|
||||||
|
|
||||||
stb_lexer lexer;
|
int nextLineFrom = 0;
|
||||||
char string_store[512];
|
LineRange curr = { 0 };
|
||||||
stb_c_lexer_init(&lexer, example_code, example_code+file_size, string_store, sizeof(string_store));
|
while (GetNextLine(&curr, contents, fileSize, nextLineFrom)) {
|
||||||
while (stb_c_lexer_get_token(&lexer)) {
|
int lineSize = curr.to - curr.from;
|
||||||
if (lexer.token != CLEX_id) continue;
|
char line[512] = { 0 };
|
||||||
|
strncpy(line, &contents[curr.from], lineSize); // `raylib_parser.c` expects lines to be null-terminated
|
||||||
|
if (IsLineImplementationIfdef(line, lineSize)) break;
|
||||||
|
|
||||||
int func_idx = get_func_from_identifier(lexer.string, funcs, func_count);
|
if (IsLineAPIFunction(line, lineSize)) {
|
||||||
if (func_idx != -1) {
|
ParseAPIFunctionInfo(line, lineSize, &functions[count]);
|
||||||
stb_lex_location loc;
|
count++;
|
||||||
stb_c_lexer_get_location(&lexer, lexer.where_firstchar, &loc);
|
if (count == maxFunctions) break;
|
||||||
int *usage_count = &usage_counts[func_idx];
|
}
|
||||||
assert(*usage_count < MAX_FUNC_USAGES);
|
|
||||||
function_usage *usage = &usages[func_idx][*usage_count];
|
|
||||||
usage->line_number = loc.line_number;
|
|
||||||
usage->line_offset = loc.line_offset;
|
|
||||||
strncpy(usage->filename, file_path, strlen(file_path));
|
|
||||||
(*usage_count)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(example_code);
|
nextLineFrom = curr.to+1;
|
||||||
fclose(file);
|
}
|
||||||
|
|
||||||
return true;
|
free(contents);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_function_usages_from_folder(char *cwd, char *dir, function_usage *usages[], int *usage_counts, function_info *funcs, int func_count) {
|
static int ParseFunctionsDefinitionsFromFolder(char *dir, FunctionInfo *functions, int maxFunctions)
|
||||||
char dir_path[PATH_MAX];
|
{
|
||||||
snprintf(dir_path, sizeof(dir_path), "%s/%s", cwd, dir);
|
DIR *dirp = opendir(dir);
|
||||||
DIR *dirp = opendir(dir_path);
|
if (dirp == NULL) {
|
||||||
if (dirp == NULL) {
|
fprintf(stderr, "Failed to open directory '%s'\n", dir);
|
||||||
fprintf(stderr, "Failed to open directory '%s'\n", dir_path);
|
return -1;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct dirent *entry;
|
int count = 0;
|
||||||
while ((entry = readdir(dirp)) != NULL) {
|
struct dirent *entry;
|
||||||
if (entry->d_type != DT_REG) continue;
|
while ((entry = readdir(dirp)) != NULL) {
|
||||||
|
if (entry->d_type != DT_REG) continue;
|
||||||
|
|
||||||
char *extension = strrchr(entry->d_name, '.');
|
char *fileExtension = strrchr(entry->d_name, '.');
|
||||||
if (!strcmp(extension, ".c")) {
|
if (fileExtension == NULL) continue;
|
||||||
char file_path[PATH_MAX];
|
if (strcmp(fileExtension, ".h")) continue;
|
||||||
snprintf(file_path, sizeof(file_path), "%s/%s", dir, entry->d_name);
|
|
||||||
collect_function_usages_from_file(cwd, file_path, usages, usage_counts, funcs, func_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dirp);
|
char path[256];
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", dir, entry->d_name);
|
||||||
|
count += ParseFunctionsDefinitionsFromHeader(path, functions + count, maxFunctions - count);
|
||||||
|
}
|
||||||
|
closedir(dirp);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
static int GetUniqueFilenames(FunctionUsage *usages, int usageCount, char *uniqueFilenames[])
|
||||||
if (argc != 4) {
|
{
|
||||||
printf("Usage: %s <raylib_api.txt> <examples-dir> <output-file>\n", argv[0]);
|
int count = 0;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *raylib_api_path = argv[1];
|
for (int i = 0; i < usageCount; i++) {
|
||||||
char *raylib_examples_path = argv[2];
|
FunctionUsage *usage = &usages[i];
|
||||||
char *output_path = argv[3];
|
|
||||||
|
|
||||||
char *output_extension = strrchr(output_path, '.');
|
bool found = false;
|
||||||
if (output_extension == NULL) {
|
for (int j = 0; j < count; j++) {
|
||||||
fprintf(stderr, "ERROR: Missing extension on output file\n");
|
if (!strcmp(uniqueFilenames[j], usage->filename)) {
|
||||||
return -1;
|
found = true;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function_info funcs[MAX_FUNCS_TO_PARSE];
|
if (!found) {
|
||||||
int funcs_count = 0;
|
uniqueFilenames[count] = strdup(usage->filename);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{ // Collect function definitions
|
return count;
|
||||||
FILE *raylib_api_file = fopen(raylib_api_path, "r");
|
}
|
||||||
if (raylib_api_file == NULL) {
|
|
||||||
fprintf(stderr, "Failed to open file '%s'\n", raylib_api_path);
|
static int OutputFunctionUsagesJSON(char *output, FunctionInfo *functions, int functionCount, FunctionUsage **usages, int *usageCounts)
|
||||||
return -1;
|
{
|
||||||
}
|
FILE *outputFile = fopen(output, "w");
|
||||||
int raylib_api_size = get_file_size(raylib_api_file);
|
if (outputFile == NULL) {
|
||||||
char *raylib_api = malloc(raylib_api_size);
|
fprintf(stderr, "Failed to open file '%s\n'", output);
|
||||||
fread(raylib_api, sizeof(char), raylib_api_size, raylib_api_file);
|
return -1;
|
||||||
fclose(raylib_api_file);
|
}
|
||||||
|
|
||||||
funcs_count = parse_funcs_from_raylib_api(raylib_api, raylib_api_size, funcs, MAX_FUNCS_TO_PARSE);
|
fwrite("{", sizeof(char), 1, outputFile);
|
||||||
|
for (int functionIndex = 0; functionIndex < functionCount; functionIndex++) {
|
||||||
free(raylib_api);
|
FunctionInfo *info = &functions[functionIndex];
|
||||||
}
|
|
||||||
|
fwrite("\"", sizeof(char), 1, outputFile);
|
||||||
function_usage *usages[MAX_FUNCS_TO_PARSE] = { 0 };
|
fwrite(info->name, sizeof(char), strlen(info->name), outputFile);
|
||||||
for (int i = 0; i < funcs_count; i++) {
|
fwrite("\":[", sizeof(char), 3, outputFile);
|
||||||
usages[i] = malloc(MAX_FUNC_USAGES * sizeof(function_usage));
|
|
||||||
}
|
int usageCount = usageCounts[functionIndex];
|
||||||
int usage_counts[MAX_FUNCS_TO_PARSE] = { 0 };
|
if (usageCount > 0) {
|
||||||
|
char *uniqueFilenames[usageCount];
|
||||||
{ // Collect function usages
|
int uniqueCount = GetUniqueFilenames(usages[functionIndex], usageCount, uniqueFilenames);
|
||||||
DIR *dirp = opendir(raylib_examples_path);
|
|
||||||
if (dirp == NULL) {
|
for (int i = 0; i < uniqueCount; i++) {
|
||||||
fprintf(stderr, "Failed to open directory '%s'\n", raylib_examples_path);
|
char *filename = uniqueFilenames[i];
|
||||||
return -1;
|
char *example_name = strchr(filename, '/')+1;
|
||||||
}
|
int example_name_size = strchr(filename, '.') - example_name;
|
||||||
struct dirent *entry;
|
|
||||||
while ((entry = readdir(dirp)) != NULL) {
|
fwrite("\"", sizeof(char), 1, outputFile);
|
||||||
if (entry->d_type != DT_DIR) continue;
|
fwrite(example_name, sizeof(char), example_name_size, outputFile);
|
||||||
if (entry->d_name[0] == '.') continue;
|
fwrite("\"", sizeof(char), 1, outputFile);
|
||||||
|
if (i < uniqueCount-1) {
|
||||||
collect_function_usages_from_folder(raylib_examples_path, entry->d_name, usages, usage_counts, funcs, funcs_count);
|
fwrite(",", sizeof(char), 1, outputFile);
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
}
|
||||||
}
|
|
||||||
|
for (int i = 0; i < uniqueCount; i++) {
|
||||||
// Output function usages
|
free(uniqueFilenames[i]);
|
||||||
FILE *output_file = fopen(output_path, "w");
|
}
|
||||||
if (output_file == NULL) {
|
}
|
||||||
fprintf(stderr, "Failed to open file '%s\n'", output_path);
|
|
||||||
return -1;
|
fwrite("]", sizeof(char), 1, outputFile);
|
||||||
}
|
if (functionIndex < functionCount-1) {
|
||||||
|
fwrite(",", sizeof(char), 1, outputFile);
|
||||||
fwrite("{\n", sizeof(char), 2, output_file);
|
}
|
||||||
for (int func_idx = 0; func_idx < funcs_count; func_idx++) {
|
}
|
||||||
function_info *info = &funcs[func_idx];
|
|
||||||
|
fwrite("}", sizeof(char), 1, outputFile);
|
||||||
fwrite("\t\"", sizeof(char), 2, output_file);
|
fclose(outputFile);
|
||||||
fwrite(info->name, sizeof(char), info->name_size, output_file);
|
|
||||||
fwrite("\": [", sizeof(char), 4, output_file);
|
return 0;
|
||||||
|
}
|
||||||
int usage_count = usage_counts[func_idx];
|
|
||||||
if (usage_count > 0) {
|
int main(int argc, char **argv)
|
||||||
fwrite("\n", sizeof(char), 1, output_file);
|
{
|
||||||
|
if (argc != 4) {
|
||||||
for (int i = 0; i < usage_count; i++) {
|
printf("Usage: %s <raylib-src-dir> <examples-dir> <output-file>\n", argv[0]);
|
||||||
function_usage *usage = &usages[func_idx][i];
|
return -1;
|
||||||
char *example_name = strchr(usage->filename, '/')+1;
|
}
|
||||||
int example_name_size = strchr(usage->filename, '.') - example_name;
|
|
||||||
|
char *raylibSrc = argv[1];
|
||||||
char *entry_format = ""
|
char *raylibExamplesPath = argv[2];
|
||||||
"\t\t{\n"
|
char *outputPath = argv[3];
|
||||||
"\t\t\t\"exampleName\": \"%.*s\",\n"
|
|
||||||
"\t\t\t\"lineNumber\": %d,\n"
|
FunctionInfo functions[MAX_FUNCS_TO_PARSE];
|
||||||
"\t\t\t\"lineOffset\": %d\n"
|
int functionCount = ParseFunctionsDefinitionsFromFolder(raylibSrc, functions, MAX_FUNCS_TO_PARSE);
|
||||||
"\t\t}";
|
if (functionCount < 0) {
|
||||||
char entry[1024];
|
return -1;
|
||||||
int entry_size = snprintf(entry, sizeof(entry), entry_format,
|
}
|
||||||
example_name_size,
|
|
||||||
example_name,
|
FunctionUsage *usages[MAX_FUNCS_TO_PARSE] = { 0 };
|
||||||
usage->line_number,
|
for (int i = 0; i < functionCount; i++) {
|
||||||
usage->line_offset);
|
usages[i] = malloc(MAX_FUNCS_PER_EXAMPLE * sizeof(FunctionUsage));
|
||||||
|
}
|
||||||
fwrite(entry, sizeof(char), entry_size, output_file);
|
int usageCounts[MAX_FUNCS_TO_PARSE] = { 0 };
|
||||||
if (i < usage_count-1) {
|
|
||||||
fwrite(",", sizeof(char), 1, output_file);
|
{ // Collect function usages from examples
|
||||||
}
|
DIR *dirp = opendir(raylibExamplesPath);
|
||||||
fwrite("\n", sizeof(char), 1, output_file);
|
if (dirp == NULL) {
|
||||||
}
|
fprintf(stderr, "Failed to open directory '%s'\n", raylibExamplesPath);
|
||||||
|
return -1;
|
||||||
fwrite("\t", sizeof(char), 1, output_file);
|
}
|
||||||
}
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dirp)) != NULL) {
|
||||||
fwrite("]", sizeof(char), 1, output_file);
|
if (entry->d_type != DT_DIR) continue;
|
||||||
if (func_idx < funcs_count-1) {
|
if (entry->d_name[0] == '.') continue;
|
||||||
fwrite(",", sizeof(char), 1, output_file);
|
|
||||||
}
|
ParseFunctionsUsagesFromFolder(raylibExamplesPath, entry->d_name, usages, usageCounts, functions, functionCount);
|
||||||
fwrite("\n", sizeof(char), 1, output_file);
|
}
|
||||||
}
|
closedir(dirp);
|
||||||
|
}
|
||||||
fwrite("}", sizeof(char), 1, output_file);
|
|
||||||
fclose(output_file);
|
// Output function usages
|
||||||
|
OutputFunctionUsagesJSON(outputPath, functions, functionCount, usages, usageCounts);
|
||||||
for (int i = 0; i < funcs_count; i++) {
|
|
||||||
free(usages[i]);
|
for (int i = 0; i < functionCount; i++) {
|
||||||
}
|
free(usages[i]);
|
||||||
|
}
|
||||||
return 0;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
502
src/raylib_parser.c
Normal file
502
src/raylib_parser.c
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
/**********************************************************************************************
|
||||||
|
|
||||||
|
raylib API parser
|
||||||
|
|
||||||
|
This parser scans raylib.h to get API information about defines, structs, aliases, enums, callbacks and functions.
|
||||||
|
All data is divided into pieces, usually as strings. The following types are used for data:
|
||||||
|
|
||||||
|
- struct DefineInfo
|
||||||
|
- struct StructInfo
|
||||||
|
- struct AliasInfo
|
||||||
|
- struct EnumInfo
|
||||||
|
- struct FunctionInfo
|
||||||
|
|
||||||
|
CONSTRAINTS:
|
||||||
|
|
||||||
|
This parser is specifically designed to work with raylib.h, so, it has some constraints:
|
||||||
|
|
||||||
|
- Functions are expected as a single line with the following structure:
|
||||||
|
|
||||||
|
<retType> <name>(<paramType[0]> <paramName[0]>, <paramType[1]> <paramName[1]>); <desc>
|
||||||
|
|
||||||
|
Be careful with functions broken into several lines, it breaks the process!
|
||||||
|
|
||||||
|
- Structures are expected as several lines with the following form:
|
||||||
|
|
||||||
|
<desc>
|
||||||
|
typedef struct <name> {
|
||||||
|
<fieldType[0]> <fieldName[0]>; <fieldDesc[0]>
|
||||||
|
<fieldType[1]> <fieldName[1]>; <fieldDesc[1]>
|
||||||
|
<fieldType[2]> <fieldName[2]>; <fieldDesc[2]>
|
||||||
|
} <name>;
|
||||||
|
|
||||||
|
- Enums are expected as several lines with the following form:
|
||||||
|
|
||||||
|
<desc>
|
||||||
|
typedef enum {
|
||||||
|
<valueName[0]> = <valueInteger[0]>, <valueDesc[0]>
|
||||||
|
<valueName[1]>,
|
||||||
|
<valueName[2]>, <valueDesc[2]>
|
||||||
|
<valueName[3]> <valueDesc[3]>
|
||||||
|
} <name>;
|
||||||
|
|
||||||
|
NOTE: Multiple options are supported for enums:
|
||||||
|
- If value is not provided, (<valueInteger[i -1]> + 1) is assigned
|
||||||
|
- Value description can be provided or not
|
||||||
|
|
||||||
|
OTHER NOTES:
|
||||||
|
|
||||||
|
- This parser could work with other C header files if mentioned constraints are followed.
|
||||||
|
- This parser does not require <string.h> library, all data is parsed directly from char buffers.
|
||||||
|
- This is a modified/stripped down version of the original parser
|
||||||
|
Done so to make it usable as a library, and not a CLI tool.
|
||||||
|
This does not implement a function for every type to parse
|
||||||
|
|
||||||
|
LICENSE: zlib/libpng
|
||||||
|
|
||||||
|
raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
|
BSD-like license that allows static linking with closed source software:
|
||||||
|
|
||||||
|
Copyright (c) 2021-2023 Ramon Santamaria (@raysan5)
|
||||||
|
|
||||||
|
**********************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
|
#include <stdlib.h> // Required for: malloc(), calloc(), realloc(), free(), atoi(), strtol()
|
||||||
|
#include <stdio.h> // Required for: printf(), fopen(), fseek(), ftell(), fread(), fclose()
|
||||||
|
#include <stdbool.h> // Required for: bool
|
||||||
|
#include <ctype.h> // Required for: isdigit()
|
||||||
|
|
||||||
|
#define MAX_LINE_LENGTH 512 // Maximum length of one line (including comments)
|
||||||
|
|
||||||
|
#define MAX_STRUCT_FIELDS 64 // Maximum number of struct fields
|
||||||
|
#define MAX_ENUM_VALUES 512 // Maximum number of enum values
|
||||||
|
#define MAX_FUNCTION_PARAMETERS 12 // Maximum number of function parameters
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Types and Structures Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Type of parsed define
|
||||||
|
typedef enum {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
MACRO,
|
||||||
|
GUARD,
|
||||||
|
INT,
|
||||||
|
INT_MATH,
|
||||||
|
LONG,
|
||||||
|
LONG_MATH,
|
||||||
|
FLOAT,
|
||||||
|
FLOAT_MATH,
|
||||||
|
DOUBLE,
|
||||||
|
DOUBLE_MATH,
|
||||||
|
CHAR,
|
||||||
|
STRING,
|
||||||
|
COLOR
|
||||||
|
} DefineType;
|
||||||
|
|
||||||
|
// Define info data
|
||||||
|
typedef struct DefineInfo {
|
||||||
|
char name[64]; // Define name
|
||||||
|
int type; // Define type
|
||||||
|
char value[256]; // Define value
|
||||||
|
char desc[128]; // Define description
|
||||||
|
bool isHex; // Define is hex number (for types INT, LONG)
|
||||||
|
} DefineInfo;
|
||||||
|
|
||||||
|
// Struct info data
|
||||||
|
typedef struct StructInfo {
|
||||||
|
char name[64]; // Struct name
|
||||||
|
char desc[128]; // Struct type description
|
||||||
|
int fieldCount; // Number of fields in the struct
|
||||||
|
char fieldType[MAX_STRUCT_FIELDS][64]; // Field type
|
||||||
|
char fieldName[MAX_STRUCT_FIELDS][64]; // Field name
|
||||||
|
char fieldDesc[MAX_STRUCT_FIELDS][128]; // Field description
|
||||||
|
} StructInfo;
|
||||||
|
|
||||||
|
// Alias info data
|
||||||
|
typedef struct AliasInfo {
|
||||||
|
char type[64]; // Alias type
|
||||||
|
char name[64]; // Alias name
|
||||||
|
char desc[128]; // Alias description
|
||||||
|
} AliasInfo;
|
||||||
|
|
||||||
|
// Enum info data
|
||||||
|
typedef struct EnumInfo {
|
||||||
|
char name[64]; // Enum name
|
||||||
|
char desc[128]; // Enum description
|
||||||
|
int valueCount; // Number of values in enumerator
|
||||||
|
char valueName[MAX_ENUM_VALUES][64]; // Value name definition
|
||||||
|
int valueInteger[MAX_ENUM_VALUES]; // Value integer
|
||||||
|
char valueDesc[MAX_ENUM_VALUES][128]; // Value description
|
||||||
|
} EnumInfo;
|
||||||
|
|
||||||
|
// Function info data
|
||||||
|
typedef struct FunctionInfo {
|
||||||
|
char name[64]; // Function name
|
||||||
|
char desc[128]; // Function description (comment at the end)
|
||||||
|
char retType[32]; // Return value type
|
||||||
|
int paramCount; // Number of function parameters
|
||||||
|
char paramType[MAX_FUNCTION_PARAMETERS][32]; // Parameters type
|
||||||
|
char paramName[MAX_FUNCTION_PARAMETERS][32]; // Parameters name
|
||||||
|
char paramDesc[MAX_FUNCTION_PARAMETERS][128]; // Parameters description
|
||||||
|
} FunctionInfo;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Global Variables Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef RAYLIB_API_DEFINE
|
||||||
|
#define RAYLIB_API_DEFINE "RLAPI"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Functions Declaration
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static bool IsLineAPIFunction(char *line, int lineSize);
|
||||||
|
static void ParseAPIFunctionInfo(char *linePtr, int lineSize, FunctionInfo *functionInfo);
|
||||||
|
|
||||||
|
static char *LoadFileText(const char *fileName, int *length);
|
||||||
|
static char **GetTextLines(const char *buffer, int length, int *linesCount);
|
||||||
|
static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name);
|
||||||
|
static void GetDescription(const char *source, char *description);
|
||||||
|
static void MoveArraySize(char *name, char *type); // Move array size from name to type
|
||||||
|
static unsigned int TextLength(const char *text); // Get text length in bytes, check for \0 character
|
||||||
|
static bool IsTextEqual(const char *text1, const char *text2, unsigned int count);
|
||||||
|
static int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string
|
||||||
|
static void MemoryCopy(void *dest, const void *src, unsigned int count);
|
||||||
|
static char *EscapeBackslashes(char *text); // Replace '\' by "\\" when exporting to JSON and XML
|
||||||
|
static const char *StrDefineType(DefineType type); // Get string of define type
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Additional functions for use as a library
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static bool IsLineAPIFunction(char *line, int lineSize)
|
||||||
|
{
|
||||||
|
int apiDefineSize = TextLength(RAYLIB_API_DEFINE);
|
||||||
|
|
||||||
|
// Read function line (starting with `define`, i.e. for raylib.h "RLAPI")
|
||||||
|
return lineSize >= apiDefineSize && IsTextEqual(line, RAYLIB_API_DEFINE, apiDefineSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes that `func_info` is zero initialized
|
||||||
|
static void ParseAPIFunctionInfo(char *linePtr, int lineSize, FunctionInfo *functionInfo)
|
||||||
|
{
|
||||||
|
int funcParamsStart = 0;
|
||||||
|
int funcEnd = 0;
|
||||||
|
|
||||||
|
// Get return type and function name from func line
|
||||||
|
for (int c = 0; c < lineSize; c++)
|
||||||
|
{
|
||||||
|
if (linePtr[c] == '(') // Starts function parameters
|
||||||
|
{
|
||||||
|
funcParamsStart = c + 1;
|
||||||
|
|
||||||
|
// At this point we have function return type and function name
|
||||||
|
char funcRetTypeName[128] = { 0 };
|
||||||
|
int dc = TextLength(RAYLIB_API_DEFINE) + 1;
|
||||||
|
int funcRetTypeNameLen = c - dc; // Substract `define` ("RLAPI " for raylib.h)
|
||||||
|
MemoryCopy(funcRetTypeName, &linePtr[dc], funcRetTypeNameLen);
|
||||||
|
|
||||||
|
GetDataTypeAndName(funcRetTypeName, funcRetTypeNameLen, functionInfo->retType, functionInfo->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get parameters from func line
|
||||||
|
for (int c = funcParamsStart; c < lineSize; c++)
|
||||||
|
{
|
||||||
|
int paramIndex = functionInfo->paramCount;
|
||||||
|
|
||||||
|
if (linePtr[c] == ',') // Starts function parameters
|
||||||
|
{
|
||||||
|
// Get parameter type + name, extract info
|
||||||
|
char funcParamTypeName[128] = { 0 };
|
||||||
|
int funcParamTypeNameLen = c - funcParamsStart;
|
||||||
|
MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
|
||||||
|
|
||||||
|
GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, functionInfo->paramType[paramIndex], functionInfo->paramName[paramIndex]);
|
||||||
|
|
||||||
|
funcParamsStart = c + 1;
|
||||||
|
if (linePtr[c + 1] == ' ') funcParamsStart += 1;
|
||||||
|
functionInfo->paramCount++; // Move to next parameter
|
||||||
|
}
|
||||||
|
else if (linePtr[c] == ')')
|
||||||
|
{
|
||||||
|
funcEnd = c + 2;
|
||||||
|
|
||||||
|
// Check if previous word is void
|
||||||
|
if ((linePtr[c - 4] == 'v') && (linePtr[c - 3] == 'o') && (linePtr[c - 2] == 'i') && (linePtr[c - 1] == 'd')) break;
|
||||||
|
|
||||||
|
// Get parameter type + name, extract info
|
||||||
|
char funcParamTypeName[128] = { 0 };
|
||||||
|
int funcParamTypeNameLen = c - funcParamsStart;
|
||||||
|
MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
|
||||||
|
|
||||||
|
GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, functionInfo->paramType[paramIndex], functionInfo->paramName[paramIndex]);
|
||||||
|
|
||||||
|
functionInfo->paramCount++; // Move to next parameter
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get function description
|
||||||
|
GetDescription(&linePtr[funcEnd], functionInfo->desc);
|
||||||
|
|
||||||
|
// Move array sizes from name to type
|
||||||
|
for (int j = 0; j < functionInfo->paramCount; j++)
|
||||||
|
{
|
||||||
|
MoveArraySize(functionInfo->paramName[j], functionInfo->paramType[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Functions Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Load text data from file, returns a '\0' terminated string
|
||||||
|
// NOTE: text chars array should be freed manually
|
||||||
|
static char *LoadFileText(const char *fileName, int *length)
|
||||||
|
{
|
||||||
|
char *text = NULL;
|
||||||
|
|
||||||
|
if (fileName != NULL)
|
||||||
|
{
|
||||||
|
FILE *file = fopen(fileName, "rt");
|
||||||
|
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
// WARNING: When reading a file as 'text' file,
|
||||||
|
// text mode causes carriage return-linefeed translation...
|
||||||
|
// ...but using fseek() should return correct byte-offset
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
int size = ftell(file);
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
text = (char *)calloc((size + 1), sizeof(char));
|
||||||
|
unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
|
||||||
|
|
||||||
|
// WARNING: \r\n is converted to \n on reading, so,
|
||||||
|
// read bytes count gets reduced by the number of lines
|
||||||
|
if (count < (unsigned int)size)
|
||||||
|
{
|
||||||
|
text = realloc(text, count + 1);
|
||||||
|
*length = count;
|
||||||
|
}
|
||||||
|
else *length = size;
|
||||||
|
|
||||||
|
// Zero-terminate the string
|
||||||
|
text[count] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all lines from a text buffer (expecting lines ending with '\n')
|
||||||
|
static char **GetTextLines(const char *buffer, int length, int *linesCount)
|
||||||
|
{
|
||||||
|
// Get the number of lines in the text
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < length; i++) if (buffer[i] == '\n') count++;
|
||||||
|
|
||||||
|
printf("Number of text lines in buffer: %i\n", count);
|
||||||
|
|
||||||
|
// Allocate as many pointers as lines
|
||||||
|
char **lines = (char **)malloc(count*sizeof(char **));
|
||||||
|
|
||||||
|
char *bufferPtr = (char *)buffer;
|
||||||
|
|
||||||
|
for (int i = 0; (i < count) || (bufferPtr[0] != '\0'); i++)
|
||||||
|
{
|
||||||
|
lines[i] = (char *)calloc(MAX_LINE_LENGTH, sizeof(char));
|
||||||
|
|
||||||
|
// Remove line leading spaces
|
||||||
|
// Find last index of space/tab character
|
||||||
|
int index = 0;
|
||||||
|
while ((bufferPtr[index] == ' ') || (bufferPtr[index] == '\t')) index++;
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
while (bufferPtr[index + j] != '\n')
|
||||||
|
{
|
||||||
|
lines[i][j] = bufferPtr[index + j];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferPtr += (index + j + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
*linesCount = count;
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get data type and name from a string containing both
|
||||||
|
// NOTE: Useful to parse function parameters and struct fields
|
||||||
|
static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name)
|
||||||
|
{
|
||||||
|
for (int k = typeNameLen; k > 0; k--)
|
||||||
|
{
|
||||||
|
if ((typeName[k] == ' ') && (typeName[k - 1] != ','))
|
||||||
|
{
|
||||||
|
// Function name starts at this point (and ret type finishes at this point)
|
||||||
|
MemoryCopy(type, typeName, k);
|
||||||
|
MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (typeName[k] == '*')
|
||||||
|
{
|
||||||
|
MemoryCopy(type, typeName, k + 1);
|
||||||
|
MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((typeName[k] == '.') && (typeNameLen == 3)) // Handle varargs ...);
|
||||||
|
{
|
||||||
|
MemoryCopy(type, "...", 3);
|
||||||
|
MemoryCopy(name, "args", 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get comment from a line, do nothing if no comment in line
|
||||||
|
static void GetDescription(const char *line, char *description)
|
||||||
|
{
|
||||||
|
int c = 0;
|
||||||
|
int descStart = -1;
|
||||||
|
int lastSlash = -2;
|
||||||
|
bool isValid = false;
|
||||||
|
while (line[c] != '\0')
|
||||||
|
{
|
||||||
|
if (isValid && (descStart == -1) && (line[c] != ' ')) descStart = c;
|
||||||
|
else if (line[c] == '/')
|
||||||
|
{
|
||||||
|
if (lastSlash == c - 1) isValid = true;
|
||||||
|
lastSlash = c;
|
||||||
|
}
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
if (descStart != -1) MemoryCopy(description, &line[descStart], c - descStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move array size from name to type
|
||||||
|
static void MoveArraySize(char *name, char *type)
|
||||||
|
{
|
||||||
|
int nameLength = TextLength(name);
|
||||||
|
if (name[nameLength - 1] == ']')
|
||||||
|
{
|
||||||
|
for (int k = nameLength; k > 0; k--)
|
||||||
|
{
|
||||||
|
if (name[k] == '[')
|
||||||
|
{
|
||||||
|
int sizeLength = nameLength - k;
|
||||||
|
MemoryCopy(&type[TextLength(type)], &name[k], sizeLength);
|
||||||
|
name[k] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get text length in bytes, check for \0 character
|
||||||
|
static unsigned int TextLength(const char *text)
|
||||||
|
{
|
||||||
|
unsigned int length = 0;
|
||||||
|
|
||||||
|
if (text != NULL) while (*text++) length++;
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two text strings, requires number of characters to compare
|
||||||
|
static bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (text1[i] != text2[i])
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find first text occurrence within a string
|
||||||
|
int TextFindIndex(const char *text, const char *find)
|
||||||
|
{
|
||||||
|
int textLen = TextLength(text);
|
||||||
|
int findLen = TextLength(find);
|
||||||
|
|
||||||
|
for (int i = 0; i <= textLen - findLen; i++)
|
||||||
|
{
|
||||||
|
if (IsTextEqual(&text[i], find, findLen)) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom memcpy() to avoid <string.h>
|
||||||
|
static void MemoryCopy(void *dest, const void *src, unsigned int count)
|
||||||
|
{
|
||||||
|
char *srcPtr = (char *)src;
|
||||||
|
char *destPtr = (char *)dest;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; i++) destPtr[i] = srcPtr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape backslashes in a string, writing the escaped string into a static buffer
|
||||||
|
static char *EscapeBackslashes(char *text)
|
||||||
|
{
|
||||||
|
static char buffer[256] = { 0 };
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; (text[i] != '\0') && (i < 255); i++, count++)
|
||||||
|
{
|
||||||
|
buffer[count] = text[i];
|
||||||
|
|
||||||
|
if (text[i] == '\\')
|
||||||
|
{
|
||||||
|
buffer[count + 1] = '\\';
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[count] = '\0';
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get string of define type
|
||||||
|
static const char *StrDefineType(DefineType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case UNKNOWN: return "UNKNOWN";
|
||||||
|
case GUARD: return "GUARD";
|
||||||
|
case MACRO: return "MACRO";
|
||||||
|
case INT: return "INT";
|
||||||
|
case INT_MATH: return "INT_MATH";
|
||||||
|
case LONG: return "LONG";
|
||||||
|
case LONG_MATH: return "LONG_MATH";
|
||||||
|
case FLOAT: return "FLOAT";
|
||||||
|
case FLOAT_MATH: return "FLOAT_MATH";
|
||||||
|
case DOUBLE: return "DOUBLE";
|
||||||
|
case DOUBLE_MATH: return "DOUBLE_MATH";
|
||||||
|
case CHAR: return "CHAR";
|
||||||
|
case STRING: return "STRING";
|
||||||
|
case COLOR: return "COLOR";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user