2024-11-22 13:45:03 +00:00
|
|
|
#ifndef SORM_CLI_H
|
|
|
|
#define SORM_CLI_H
|
2024-11-22 14:51:47 +00:00
|
|
|
#include "sorm.h"
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <readline/history.h>
|
|
|
|
#include <readline/readline.h>
|
|
|
|
#include <rlib.h>
|
2024-11-22 13:45:03 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
2024-11-22 14:51:47 +00:00
|
|
|
#include <unistd.h>
|
2024-11-22 13:45:03 +00:00
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
const char *history_filename = ".sorm_history";
|
2024-11-22 13:45:03 +00:00
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
int _sorm_readline_accept_line(int count, int key) {
|
2024-11-22 13:45:03 +00:00
|
|
|
if (strchr(rl_line_buffer, ';')) {
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
rl_done = 1;
|
2024-11-22 13:45:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rl_insert_text("\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
char *_sorm_autocompletion_generator(const char *text, int state) {
|
|
|
|
const char *completions[] = {"exit", sorm_last_query, sorm_last_query_expanded, "python", "history", "memory", "truncate", NULL};
|
|
|
|
int list_index;
|
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
if (!state) {
|
|
|
|
list_index = 0;
|
|
|
|
}
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
while (completions[list_index] != NULL) {
|
|
|
|
if (strncmp(completions[list_index], text, strlen(text)) == 0) {
|
|
|
|
return strdup(completions[list_index++]);
|
|
|
|
}
|
|
|
|
list_index++;
|
|
|
|
}
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
char **_sorm_autocomplete(const char *text, int start, int end) {
|
2024-11-22 13:45:03 +00:00
|
|
|
rl_attempted_completion_over = 1;
|
|
|
|
return rl_completion_matches(text, _sorm_autocompletion_generator);
|
|
|
|
}
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
int _hs_read_file(const char *filename, char *buffer, size_t size) {
|
2024-11-22 13:45:03 +00:00
|
|
|
int fd;
|
|
|
|
ssize_t bytes_read;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY);
|
2024-11-22 13:45:03 +00:00
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
bytes_read = read(fd, buffer, size);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
if (bytes_read < 0 || (size_t)bytes_read != size)
|
|
|
|
return -1;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
int sorm_cli_history_dump(const char *filename) {
|
2024-11-22 13:45:03 +00:00
|
|
|
register int line_start, line_end;
|
|
|
|
char *input;
|
|
|
|
struct stat finfo;
|
|
|
|
size_t file_size;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
if (stat(filename, &finfo) < 0)
|
2024-11-22 13:45:03 +00:00
|
|
|
return -1;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
file_size = (size_t)finfo.st_size;
|
2024-11-22 14:51:47 +00:00
|
|
|
input = (char *)malloc(file_size + 1);
|
2024-11-22 13:45:03 +00:00
|
|
|
if (!input)
|
|
|
|
return -1;
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
if (_hs_read_file(filename, input, file_size) < 0) {
|
|
|
|
free(input);
|
2024-11-22 13:45:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
input[file_size] = '\0';
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
for (line_start = line_end = 0; line_end < file_size; line_end++) {
|
|
|
|
if (input[line_end] == '\n') {
|
|
|
|
input[line_end] = '\0';
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
printf("%s\n", input + line_start);
|
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
line_start = line_end + 1;
|
|
|
|
}
|
|
|
|
}
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
if (line_start < file_size)
|
2024-11-22 14:51:47 +00:00
|
|
|
printf("%s\n", input + line_start);
|
|
|
|
|
|
|
|
free(input);
|
|
|
|
|
|
|
|
return where_history();
|
2024-11-22 13:45:03 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
char sorm_history_filename[4096];
|
2024-11-22 13:45:03 +00:00
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
void sorm_cli_init(const char *history_filename) {
|
2024-11-22 13:45:03 +00:00
|
|
|
strcpy(sorm_history_filename, history_filename);
|
2024-11-22 14:51:47 +00:00
|
|
|
rl_bind_key('\n', NULL);
|
2024-11-22 13:45:03 +00:00
|
|
|
rl_bind_key('\r', NULL);
|
|
|
|
rl_add_defun("custom-accept-line", _sorm_readline_accept_line, '\n');
|
|
|
|
rl_add_defun("custom-accept-line-cr", _sorm_readline_accept_line, '\r');
|
|
|
|
rl_variable_bind("enable-bracketed-paste", "on");
|
|
|
|
rl_attempted_completion_function = _sorm_autocomplete;
|
|
|
|
rl_variable_bind("show-all-if-ambiguous", "on");
|
|
|
|
rl_variable_bind("menu-complete-display-prefix", "on");
|
2024-11-22 14:51:47 +00:00
|
|
|
|
2024-11-22 13:45:03 +00:00
|
|
|
using_history();
|
2024-11-22 14:51:47 +00:00
|
|
|
read_history(sorm_history_filename);
|
2024-11-22 13:45:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char sorm_cli_previous_command[sizeof(rl_line_buffer)];
|
2024-11-22 14:51:47 +00:00
|
|
|
char *sorm_cli_readline(char *prompt) {
|
|
|
|
char *result = readline(prompt);
|
|
|
|
if (strcmp(rl_line_buffer, sorm_cli_previous_command)) {
|
|
|
|
add_history((const char *)result);
|
|
|
|
write_history(sorm_history_filename);
|
|
|
|
}
|
|
|
|
strcpy(sorm_cli_previous_command, rl_line_buffer);
|
2024-11-22 13:45:03 +00:00
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
return result;
|
|
|
|
}
|
2024-11-22 13:45:03 +00:00
|
|
|
|
2024-11-22 14:51:47 +00:00
|
|
|
bool sormrepl_handle_command(char *command) {
|
|
|
|
if (!strncmp(command, "history", 7)) {
|
|
|
|
sorm_cli_history_dump(history_filename);
|
|
|
|
return true;
|
|
|
|
}
|
2024-11-22 13:45:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
2024-11-22 14:51:47 +00:00
|
|
|
void sormrepl(int sorm) {
|
|
|
|
sorm_t *db = sormg(sorm);
|
2024-11-22 13:45:03 +00:00
|
|
|
char sql[4097];
|
|
|
|
|
|
|
|
sorm_cli_init(history_filename);
|
2024-11-22 14:51:47 +00:00
|
|
|
char *query;
|
|
|
|
while ((query = sorm_cli_readline("sql> "))) {
|
|
|
|
if (sormrepl_handle_command(query))
|
2024-11-22 13:45:03 +00:00
|
|
|
continue;
|
2024-11-22 14:51:47 +00:00
|
|
|
sorm_ptr res = sormq(sorm, query);
|
|
|
|
if (res) {
|
|
|
|
if (sormqt(query) == SORM_SELECT) {
|
|
|
|
int length = sormlv(res);
|
|
|
|
sormfmtd(res);
|
|
|
|
free(res);
|
|
|
|
} else if (sormqt(query) == SORM_DELETE) {
|
|
|
|
printf("%d records affected.\n", res);
|
|
|
|
} else if (sormqt(query) == SORM_INSERT) {
|
|
|
|
printf("Last insert id: %d.\n", res);
|
2024-11-22 13:45:03 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-22 14:51:47 +00:00
|
|
|
|
|
|
|
printf("Rows: %lld, Execute %s, Format: %s\n", sorm_row_count, format_time(_sorm_query_duration),
|
|
|
|
format_time(_sorm_result_format_duration));
|
|
|
|
printf("%s\n", rmalloc_stats());
|
|
|
|
}
|
2024-11-22 13:45:03 +00:00
|
|
|
}
|
|
|
|
#endif
|