#ifndef SORM_CLI_H #define SORM_CLI_H #include "sorm.h" #include #include #include #include #include #include #include #include #include const char *history_filename = ".sorm_history"; int _sorm_readline_accept_line(int count, int key) { (void)count; (void)key; if (strchr(rl_line_buffer, ';')) { rl_done = 1; return 0; } rl_insert_text("\n"); return 0; } 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; if (!state) { list_index = 0; } while (completions[list_index] != NULL) { if (strncmp(completions[list_index], text, strlen(text)) == 0) { return strdup(completions[list_index++]); } list_index++; } return NULL; } char **_sorm_autocomplete(const char *text, int start, int end) { (void)start; (void)end; rl_attempted_completion_over = 1; return rl_completion_matches(text, _sorm_autocompletion_generator); } int _hs_read_file(const char *filename, char *buffer, size_t size) { int fd; ssize_t bytes_read; fd = open(filename, O_RDONLY); if (fd < 0) return -1; bytes_read = read(fd, buffer, size); close(fd); if (bytes_read < 0 || (size_t)bytes_read != size) return -1; return 0; } int sorm_cli_history_dump(const char *filename) { register int line_start, line_end; char *input; struct stat finfo; size_t file_size; if (stat(filename, &finfo) < 0) return -1; file_size = (size_t)finfo.st_size; input = (char *)malloc(file_size + 1); if (!input) return -1; if (_hs_read_file(filename, input, file_size) < 0) { free(input); return -1; } input[file_size] = '\0'; for (line_start = line_end = 0; (size_t)line_end < file_size; line_end++) { if (input[line_end] == '\n') { input[line_end] = '\0'; printf("%s\n", input + line_start); line_start = line_end + 1; } } if ((size_t)line_start < file_size) printf("%s\n", input + line_start); free(input); return where_history(); } char sorm_history_filename[4096]; void sorm_cli_init(const char *history_filename) { strcpy(sorm_history_filename, history_filename); rl_bind_key('\n', NULL); 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"); using_history(); read_history(sorm_history_filename); } char sorm_cli_previous_command[sizeof(rl_line_buffer)]; 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); return result; } bool sormrepl_handle_command(char *command) { if (!strncmp(command, "history", 7)) { sorm_cli_history_dump(history_filename); return true; } return false; } void sormrepl(int sorm) { sorm_cli_init(history_filename); char *query; while ((query = sorm_cli_readline("sql> "))) { if (sormrepl_handle_command(query)) continue; sorm_ptr res = sormq(sorm, query); if (res) { if (sormqt(query) == SORM_SELECT) { 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); } } 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()); } } #endif