Update.
This commit is contained in:
parent
66f17da391
commit
c1450415eb
123
src/line.h
Executable file
123
src/line.h
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
// Written by retoor@molodetz.nl
|
||||||
|
|
||||||
|
// This source code provides command-line input functionalities with
|
||||||
|
// autocomplete and history features using the readline library. It allows users
|
||||||
|
// to complete commands and manage input history.
|
||||||
|
|
||||||
|
// External includes:
|
||||||
|
// - <readline/readline.h>
|
||||||
|
// - <readline/history.h>
|
||||||
|
// - <glob.h>
|
||||||
|
|
||||||
|
// MIT License: Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction.
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <glob.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#define HISTORY_FILE "~/.r_history"
|
||||||
|
|
||||||
|
bool line_initialized = false;
|
||||||
|
|
||||||
|
char *get_history_file() {
|
||||||
|
static char result[4096];
|
||||||
|
result[0] = '\0';
|
||||||
|
|
||||||
|
char *expanded = expand_home_directory(HISTORY_FILE);
|
||||||
|
if (expanded == NULL) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
strncpy(result, expanded, sizeof(result) - 1);
|
||||||
|
result[sizeof(result) - 1] = '\0';
|
||||||
|
free(expanded);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line_command_generator(const char *text, int state) {
|
||||||
|
static int list_index, len = 0;
|
||||||
|
const char *commands[] = {"help", "exit", "list", "review",
|
||||||
|
"refactor", "obfuscate", "!verbose", "!dump",
|
||||||
|
"!model", "!debug", NULL};
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
list_index = 0;
|
||||||
|
len = strlen(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (commands[list_index]) {
|
||||||
|
const char *command = commands[list_index++];
|
||||||
|
if (strncmp(command, text, len) == 0) {
|
||||||
|
return strdup(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line_file_generator(const char *text, int state) {
|
||||||
|
static int list_index;
|
||||||
|
static glob_t glob_result;
|
||||||
|
static int glob_valid = 0;
|
||||||
|
char pattern[1024];
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
if (glob_valid) {
|
||||||
|
globfree(&glob_result);
|
||||||
|
glob_valid = 0;
|
||||||
|
}
|
||||||
|
list_index = 0;
|
||||||
|
snprintf(pattern, sizeof(pattern), "%s*", text);
|
||||||
|
if (glob(pattern, GLOB_NOSORT, NULL, &glob_result) == 0) {
|
||||||
|
glob_valid = 1;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glob_valid && (size_t)list_index < glob_result.gl_pathc) {
|
||||||
|
return strdup(glob_result.gl_pathv[list_index++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glob_valid) {
|
||||||
|
globfree(&glob_result);
|
||||||
|
glob_valid = 0;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **line_command_completion(const char *text, int start, int end) {
|
||||||
|
rl_attempted_completion_over = 1;
|
||||||
|
|
||||||
|
// Check if the input is a file path
|
||||||
|
if (start > 0 && text[0] != ' ') {
|
||||||
|
return rl_completion_matches(text, line_file_generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rl_completion_matches(text, line_command_generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_init() {
|
||||||
|
if (!line_initialized) {
|
||||||
|
rl_attempted_completion_function = line_command_completion;
|
||||||
|
line_initialized = true;
|
||||||
|
read_history(get_history_file());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line_read(char *prefix) {
|
||||||
|
char *data = readline(prefix);
|
||||||
|
if (!(data && *data)) {
|
||||||
|
free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_add_history(char *data) {
|
||||||
|
add_history(data);
|
||||||
|
write_history(get_history_file());
|
||||||
|
}
|
||||||
@ -7,9 +7,9 @@
|
|||||||
#include "r_error.h"
|
#include "r_error.h"
|
||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
|
|
||||||
#include "../line.h"
|
#include "line.h"
|
||||||
#include "../markdown.h"
|
#include "markdown.h"
|
||||||
#include "../utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <json-c/json.h>
|
#include <json-c/json.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|||||||
351
src/markdown.h
Executable file
351
src/markdown.h
Executable file
@ -0,0 +1,351 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// --- ANSI Escape Codes ---
|
||||||
|
#define RESET "\033[0m"
|
||||||
|
#define BOLD "\033[1m"
|
||||||
|
#define ITALIC "\033[3m"
|
||||||
|
#define STRIKETHROUGH "\033[9m"
|
||||||
|
|
||||||
|
#define FG_YELLOW "\033[33m"
|
||||||
|
#define FG_BLUE "\033[34m"
|
||||||
|
#define FG_CYAN "\033[36m"
|
||||||
|
#define FG_MAGENTA "\033[35m"
|
||||||
|
|
||||||
|
#define BG_YELLOW_FG_BLACK "\033[43;30m"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if a given word is a programming language keyword.
|
||||||
|
* * @param word The word to check.
|
||||||
|
* @return int 1 if it's a keyword, 0 otherwise.
|
||||||
|
*/
|
||||||
|
int is_keyword(const char *word) {
|
||||||
|
// A comprehensive list of keywords from various popular languages.
|
||||||
|
const char *keywords[] = {
|
||||||
|
// C keywords
|
||||||
|
"int", "float", "double", "char", "void", "if", "else", "while", "for",
|
||||||
|
"return", "struct", "printf",
|
||||||
|
// Rust keywords
|
||||||
|
"let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub",
|
||||||
|
"const", "static",
|
||||||
|
// Python keywords
|
||||||
|
"def", "class", "import", "from", "as", "with", "try", "except",
|
||||||
|
"finally", "lambda", "async", "await",
|
||||||
|
// Java keywords
|
||||||
|
"public", "private", "protected", "class", "interface", "extends",
|
||||||
|
"implements", "new", "static", "final", "synchronized",
|
||||||
|
// JavaScript keywords
|
||||||
|
"var", "let", "const", "function", "async", "await", "if", "else",
|
||||||
|
"switch", "case", "break", "continue", "return",
|
||||||
|
// C++ keywords
|
||||||
|
"namespace", "template", "typename", "class", "public", "private",
|
||||||
|
"protected", "virtual", "override", "friend", "new",
|
||||||
|
// Go keywords
|
||||||
|
"package", "import", "func", "var", "const", "type", "interface",
|
||||||
|
"struct", "go", "defer", "select",
|
||||||
|
// Bash keywords
|
||||||
|
"if", "then", "else", "elif", "fi", "case", "esac", "for", "while",
|
||||||
|
"until", "do", "done", "function",
|
||||||
|
// C# keywords
|
||||||
|
"namespace", "using", "class", "interface", "public", "private",
|
||||||
|
"protected", "static", "void", "new", "override"};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
|
||||||
|
if (strcmp(word, keywords[i]) == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Applies basic syntax highlighting to a string of code.
|
||||||
|
* * @param code The code string to highlight.
|
||||||
|
*/
|
||||||
|
void highlight_code(const char *code) {
|
||||||
|
const char *ptr = code;
|
||||||
|
char buffer[4096];
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
while (*ptr) {
|
||||||
|
// Highlight keywords
|
||||||
|
if (isalpha((unsigned char)*ptr) || *ptr == '_') {
|
||||||
|
while (isalnum((unsigned char)*ptr) || *ptr == '_') {
|
||||||
|
buffer[index++] = *ptr++;
|
||||||
|
}
|
||||||
|
buffer[index] = '\0';
|
||||||
|
|
||||||
|
if (is_keyword(buffer)) {
|
||||||
|
printf(FG_BLUE "%s" RESET FG_YELLOW, buffer);
|
||||||
|
} else {
|
||||||
|
printf("%s", buffer);
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
// Highlight numbers
|
||||||
|
} else if (isdigit((unsigned char)*ptr)) {
|
||||||
|
while (isdigit((unsigned char)*ptr)) {
|
||||||
|
buffer[index++] = *ptr++;
|
||||||
|
}
|
||||||
|
buffer[index] = '\0';
|
||||||
|
printf(FG_CYAN "%s" RESET FG_YELLOW, buffer);
|
||||||
|
index = 0;
|
||||||
|
// Print other characters as-is
|
||||||
|
} else {
|
||||||
|
putchar(*ptr);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses a Markdown string and prints it to the console with ANSI color
|
||||||
|
* codes.
|
||||||
|
*
|
||||||
|
* This version supports a wide range of Markdown features, including:
|
||||||
|
* - Headers (H1-H6)
|
||||||
|
* - Bold (**, __) and Italic (*, _) text
|
||||||
|
* - Strikethrough (~~) and Highlight (==)
|
||||||
|
* - Blockquotes (>), Nested Ordered (1.) and Unordered lists (*, -, +)
|
||||||
|
* - Inline code (`) and full code blocks (```) with syntax highlighting
|
||||||
|
* - Links ([text](url)) and Horizontal rules (---, ***)
|
||||||
|
* * @param markdown The raw Markdown string to parse.
|
||||||
|
*/
|
||||||
|
void parse_markdown_to_ansi(const char *markdown) {
|
||||||
|
const char *ptr = markdown;
|
||||||
|
bool is_start_of_line = true;
|
||||||
|
|
||||||
|
while (*ptr) {
|
||||||
|
// --- Code Blocks (```) ---
|
||||||
|
if (is_start_of_line && strncmp(ptr, "```", 3) == 0) {
|
||||||
|
ptr += 3;
|
||||||
|
while (*ptr && *ptr != '\n')
|
||||||
|
ptr++;
|
||||||
|
if (*ptr)
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
const char *code_start = ptr;
|
||||||
|
const char *code_end = strstr(code_start, "```");
|
||||||
|
|
||||||
|
if (code_end) {
|
||||||
|
char block_buffer[code_end - code_start + 1];
|
||||||
|
strncpy(block_buffer, code_start, code_end - code_start);
|
||||||
|
block_buffer[code_end - code_start] = '\0';
|
||||||
|
|
||||||
|
printf(FG_YELLOW);
|
||||||
|
highlight_code(block_buffer);
|
||||||
|
printf(RESET);
|
||||||
|
|
||||||
|
ptr = code_end + 3;
|
||||||
|
if (*ptr == '\n')
|
||||||
|
ptr++;
|
||||||
|
is_start_of_line = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
printf(FG_YELLOW);
|
||||||
|
highlight_code(code_start);
|
||||||
|
printf(RESET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Block-level Elements (checked at the start of a line) ---
|
||||||
|
if (is_start_of_line) {
|
||||||
|
const char *line_start_ptr = ptr;
|
||||||
|
int indent_level = 0;
|
||||||
|
while (*ptr == ' ') {
|
||||||
|
indent_level++;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool block_processed = true;
|
||||||
|
if (strncmp(ptr, "###### ", 7) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 7;
|
||||||
|
} else if (strncmp(ptr, "##### ", 6) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 6;
|
||||||
|
} else if (strncmp(ptr, "#### ", 5) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 5;
|
||||||
|
} else if (strncmp(ptr, "### ", 4) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 4;
|
||||||
|
} else if (strncmp(ptr, "## ", 3) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 3;
|
||||||
|
} else if (strncmp(ptr, "# ", 2) == 0) {
|
||||||
|
printf(BOLD FG_YELLOW);
|
||||||
|
ptr += 2;
|
||||||
|
} else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) &&
|
||||||
|
(*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) {
|
||||||
|
printf(FG_CYAN "───────────────────────────────────────────────────────"
|
||||||
|
"──────────" RESET "\n");
|
||||||
|
ptr += 3;
|
||||||
|
if (*ptr == '\n')
|
||||||
|
ptr++;
|
||||||
|
is_start_of_line = true;
|
||||||
|
continue;
|
||||||
|
} else if (strncmp(ptr, "> ", 2) == 0) {
|
||||||
|
for (int i = 0; i < indent_level; i++)
|
||||||
|
putchar(' ');
|
||||||
|
printf(ITALIC FG_CYAN "▎ " RESET);
|
||||||
|
ptr += 2;
|
||||||
|
is_start_of_line = false;
|
||||||
|
continue;
|
||||||
|
} else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') &&
|
||||||
|
*(ptr + 1) == ' ') {
|
||||||
|
for (int i = 0; i < indent_level; i++)
|
||||||
|
putchar(' ');
|
||||||
|
printf(FG_MAGENTA "• " RESET);
|
||||||
|
ptr += 2;
|
||||||
|
is_start_of_line = false;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
const char *temp_ptr = ptr;
|
||||||
|
while (isdigit((unsigned char)*temp_ptr))
|
||||||
|
temp_ptr++;
|
||||||
|
if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') {
|
||||||
|
for (int i = 0; i < indent_level; i++)
|
||||||
|
putchar(' ');
|
||||||
|
printf(FG_MAGENTA);
|
||||||
|
fwrite(ptr, 1, (temp_ptr - ptr) + 1, stdout);
|
||||||
|
printf(" " RESET);
|
||||||
|
ptr = temp_ptr + 2;
|
||||||
|
is_start_of_line = false;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
block_processed = false;
|
||||||
|
ptr = line_start_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block_processed) {
|
||||||
|
while (*ptr && *ptr != '\n')
|
||||||
|
putchar(*ptr++);
|
||||||
|
printf(RESET "\n");
|
||||||
|
if (*ptr == '\n')
|
||||||
|
ptr++;
|
||||||
|
is_start_of_line = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inline Elements (order is important) ---
|
||||||
|
if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) {
|
||||||
|
const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___";
|
||||||
|
printf(BOLD ITALIC);
|
||||||
|
ptr += 3;
|
||||||
|
const char *end = strstr(ptr, marker);
|
||||||
|
if (end) {
|
||||||
|
fwrite(ptr, 1, end - ptr, stdout);
|
||||||
|
ptr = end + 3;
|
||||||
|
} else {
|
||||||
|
fputs(ptr, stdout);
|
||||||
|
ptr += strlen(ptr);
|
||||||
|
}
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) {
|
||||||
|
const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__";
|
||||||
|
printf(BOLD);
|
||||||
|
ptr += 2;
|
||||||
|
const char *end = strstr(ptr, marker);
|
||||||
|
if (end) {
|
||||||
|
fwrite(ptr, 1, end - ptr, stdout);
|
||||||
|
ptr = end + 2;
|
||||||
|
} else {
|
||||||
|
fputs(ptr, stdout);
|
||||||
|
ptr += strlen(ptr);
|
||||||
|
}
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((*ptr == '*' || *ptr == '_') && !isspace(*(ptr - 1)) &&
|
||||||
|
!isspace(*(ptr + 1))) {
|
||||||
|
char marker = *ptr;
|
||||||
|
printf(ITALIC);
|
||||||
|
ptr++;
|
||||||
|
const char *start = ptr;
|
||||||
|
while (*ptr && *ptr != marker)
|
||||||
|
ptr++;
|
||||||
|
if (*ptr == marker) {
|
||||||
|
fwrite(start, 1, ptr - start, stdout);
|
||||||
|
ptr++;
|
||||||
|
} else {
|
||||||
|
putchar(marker);
|
||||||
|
ptr = start;
|
||||||
|
}
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp(ptr, "~~", 2) == 0) {
|
||||||
|
printf(STRIKETHROUGH);
|
||||||
|
ptr += 2;
|
||||||
|
const char *end = strstr(ptr, "~~");
|
||||||
|
if (end) {
|
||||||
|
fwrite(ptr, 1, end - ptr, stdout);
|
||||||
|
ptr = end + 2;
|
||||||
|
} else {
|
||||||
|
fputs(ptr, stdout);
|
||||||
|
ptr += strlen(ptr);
|
||||||
|
}
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp(ptr, "==", 2) == 0) {
|
||||||
|
printf(BG_YELLOW_FG_BLACK);
|
||||||
|
ptr += 2;
|
||||||
|
const char *end = strstr(ptr, "==");
|
||||||
|
if (end) {
|
||||||
|
fwrite(ptr, 1, end - ptr, stdout);
|
||||||
|
ptr = end + 2;
|
||||||
|
} else {
|
||||||
|
fputs(ptr, stdout);
|
||||||
|
ptr += strlen(ptr);
|
||||||
|
}
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*ptr == '`' && *(ptr + 1) != '`') {
|
||||||
|
printf(FG_YELLOW);
|
||||||
|
ptr++;
|
||||||
|
const char *start = ptr;
|
||||||
|
while (*ptr && *ptr != '`')
|
||||||
|
ptr++;
|
||||||
|
fwrite(start, 1, ptr - start, stdout);
|
||||||
|
if (*ptr == '`')
|
||||||
|
ptr++;
|
||||||
|
printf(RESET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*ptr == '[') {
|
||||||
|
const char *text_start = ptr + 1;
|
||||||
|
const char *text_end = strchr(text_start, ']');
|
||||||
|
if (text_end && *(text_end + 1) == '(') {
|
||||||
|
const char *url_start = text_end + 2;
|
||||||
|
const char *url_end = strchr(url_start, ')');
|
||||||
|
if (url_end) {
|
||||||
|
printf(FG_BLUE);
|
||||||
|
fwrite(text_start, 1, text_end - text_start, stdout);
|
||||||
|
printf(RESET " (");
|
||||||
|
printf(ITALIC FG_CYAN);
|
||||||
|
fwrite(url_start, 1, url_end - url_start, stdout);
|
||||||
|
printf(RESET ")");
|
||||||
|
ptr = url_end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Default Character ---
|
||||||
|
if (*ptr == '\n') {
|
||||||
|
is_start_of_line = true;
|
||||||
|
} else if (!isspace((unsigned char)*ptr)) {
|
||||||
|
is_start_of_line = false;
|
||||||
|
}
|
||||||
|
putchar(*ptr);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
146
src/utils.h
Executable file
146
src/utils.h
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
// Written by retoor@molodetz.nl
|
||||||
|
|
||||||
|
// This header file contains utility functions for manipulating file system
|
||||||
|
// paths, focusing primarily on expanding paths starting with '~' to the home
|
||||||
|
// directory.
|
||||||
|
|
||||||
|
// This code uses standard libraries: stdio.h, stdlib.h, and string.h, and
|
||||||
|
// conditionally includes the posix libraries pwd.h and unistd.h when expanding
|
||||||
|
// the home directory manually.
|
||||||
|
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Permission is granted to use, copy, modify, merge, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software. The license includes conditions about
|
||||||
|
// providing a copy of the license and the limitation of liability and warranty.
|
||||||
|
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void get_current_directory() {
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD length = GetCurrentDirectory(PATH_MAX, buffer);
|
||||||
|
if (length > 0 && length < PATH_MAX) {
|
||||||
|
printf("Current Directory: %s\n", buffer);
|
||||||
|
} else {
|
||||||
|
printf("Error getting current directory.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (getcwd(buffer, sizeof(buffer)) != NULL) {
|
||||||
|
printf("Current Directory: %s\n", buffer);
|
||||||
|
} else {
|
||||||
|
perror("Error getting current directory");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
char *expand_home_directory(const char *path) {
|
||||||
|
if (path[0] != '~') {
|
||||||
|
return strdup(path); // Return the original path if it doesn't start with ~
|
||||||
|
}
|
||||||
|
|
||||||
|
char *home_dir;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
home_dir = getenv("USERPROFILE"); // Get home directory on Windows
|
||||||
|
#else
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
home_dir = pw->pw_dir; // Get home directory on Linux
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (home_dir == NULL) {
|
||||||
|
return NULL; // Error getting home directory
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t home_len = strlen(home_dir);
|
||||||
|
size_t path_len = strlen(path + 1);
|
||||||
|
size_t expanded_size = home_len + path_len + 1;
|
||||||
|
char *expanded_path = (char *)malloc(expanded_size);
|
||||||
|
if (expanded_path == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(expanded_path, home_dir, home_len);
|
||||||
|
memcpy(expanded_path + home_len, path + 1, path_len);
|
||||||
|
expanded_path[home_len + path_len] = '\0';
|
||||||
|
return expanded_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long hash(const char *str) {
|
||||||
|
unsigned long hash = 5381; // Starting value
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while ((c = *str++)) {
|
||||||
|
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *joinpath(const char *base_url, const char *path) {
|
||||||
|
static char result[4096];
|
||||||
|
size_t base_len = strlen(base_url);
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
if (base_len >= sizeof(result) - 2) {
|
||||||
|
base_len = sizeof(result) - 2;
|
||||||
|
}
|
||||||
|
memcpy(result, base_url, base_len);
|
||||||
|
pos = base_len;
|
||||||
|
|
||||||
|
if (pos > 0 && result[pos - 1] != '/') {
|
||||||
|
result[pos++] = '/';
|
||||||
|
}
|
||||||
|
if (path[0] == '/') {
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t path_len = strlen(path);
|
||||||
|
if (pos + path_len >= sizeof(result)) {
|
||||||
|
path_len = sizeof(result) - pos - 1;
|
||||||
|
}
|
||||||
|
memcpy(result + pos, path, path_len);
|
||||||
|
result[pos + path_len] = '\0';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *read_file(const char *path) {
|
||||||
|
char *expanded_path = expand_home_directory(path);
|
||||||
|
if (expanded_path == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
FILE *file = fopen(expanded_path, "r");
|
||||||
|
free(expanded_path);
|
||||||
|
if (file == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
long size = ftell(file);
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
char *buffer = (char *)malloc(size + 1);
|
||||||
|
size_t read = fread(buffer, 1, size, file);
|
||||||
|
if (read == 0) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
buffer[read] = '\0';
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user