#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "db.h"
#include "utils.h"
#include "history.h"
#include "config.h"
#include "shell_integration.h"
#include "tui.h"
#define RHISTORY_VERSION "0.1.0"
// Command handlers
int cmd_search(int argc, char **argv);
int cmd_import(int argc, char **argv);
int cmd_list(int argc, char **argv);
int cmd_stats(int argc, char **argv);
int cmd_delete(int argc, char **argv);
int cmd_init(int argc, char **argv);
int cmd_config(int argc, char **argv);
int cmd_record(int argc, char **argv); // Internal
void print_help(void);
void print_version(void);
int main(int argc, char **argv) {
if (argc < 2) {
return cmd_search(argc, argv);
}
if (strcmp(argv[1], "search") == 0) {
return cmd_search(argc - 1, argv + 1);
} else if (strcmp(argv[1], "import") == 0) {
return cmd_import(argc - 1, argv + 1);
} else if (strcmp(argv[1], "list") == 0) {
return cmd_list(argc - 1, argv + 1);
} else if (strcmp(argv[1], "stats") == 0) {
return cmd_stats(argc - 1, argv + 1);
} else if (strcmp(argv[1], "delete") == 0) {
return cmd_delete(argc - 1, argv + 1);
} else if (strcmp(argv[1], "init") == 0) {
return cmd_init(argc - 1, argv + 1);
} else if (strcmp(argv[1], "config") == 0) {
return cmd_config(argc - 1, argv + 1);
} else if (strcmp(argv[1], "record") == 0) {
return cmd_record(argc - 1, argv + 1);
} else if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
print_help();
return 0;
} else if (strcmp(argv[1], "version") == 0 || strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) {
print_version();
return 0;
} else {
// Default to search if unknown command, or maybe print help?
// Prompt says "rhistory [OPTIONS] [COMMAND]".
// If it starts with -, it's options for search probably.
if (argv[1][0] == '-') {
return cmd_search(argc, argv);
}
fprintf(stderr, "Unknown command: %s\n", argv[1]);
print_help();
return 1;
}
}
void print_help(void) {
printf("rhistory %s\n", RHISTORY_VERSION);
printf("Usage: rhistory [COMMAND] [OPTIONS]\n\n");
printf("Commands:\n");
printf(" search Search history (default)\n");
printf(" import Import history from shell file\n");
printf(" list List history entries\n");
printf(" stats Show statistics\n");
printf(" delete Delete entries\n");
printf(" init Generate shell integration code\n");
printf(" config Manage configuration\n");
printf(" help Show this help\n");
printf(" version Show version\n");
}
void print_version(void) {
printf("rhistory %s\n", RHISTORY_VERSION);
}
// Database Helper
bool setup_db(void) {
char *data_dir = get_data_dir();
if (!ensure_directory_exists(data_dir)) {
fprintf(stderr, "Failed to create data directory: %s\n", data_dir);
free(data_dir);
return false;
}
char *db_path = join_path(data_dir, "history.db");
bool success = db_init(db_path);
free(db_path);
free(data_dir);
return success;
}
// Stub implementations
#include "shell_integration.h"
#include "search.h"
#include <time.h>
// ...
int cmd_search(int argc, char **argv) {
if (!setup_db()) return 1;
SearchQuery query;
memset(&query, 0, sizeof(query));
query.exit_code = -1;
query.limit = 20;
static struct option long_options[] = {
{"interactive", no_argument, 0, 'i'},
{"exit", required_argument, 0, 'e'},
{"cwd", required_argument, 0, 'c'},
{"host", required_argument, 0, 'h'},
{"session", required_argument, 0, 's'},
{"limit", required_argument, 0, 'l'},
{"reverse", no_argument, 0, 'r'},
{"cmd-only", no_argument, 0, 1001},
{0, 0, 0, 0}
};
bool interactive = false;
bool cmd_only = false;
int opt_index = 0;
int c;
// Reset getopt
optind = 1;
while ((c = getopt_long(argc, argv, "ie:c:h:s:l:r", long_options, &opt_index)) != -1) {
switch (c) {
case 'i': interactive = true; break;
case 'e': query.exit_code = atoi(optarg); break;
case 'c': query.cwd = optarg; break;
case 'h': query.hostname = optarg; break;
case 's': query.session = optarg; break;
case 'l': query.limit = atoi(optarg); break;
case 'r': query.reverse = true; break;
case 1001: cmd_only = true; break;
}
}
if (optind < argc) {
query.query_string = argv[optind];
}
if (interactive) {
tui_start(&query);
} else {
SearchResult *res = search_perform(&query);
if (!res) {
fprintf(stderr, "Search failed.\n");
db_close();
return 1;
}
for (int i = 0; i < res->count; i++) {
HistoryEntry *e = res->entries[i];
if (cmd_only) {
printf("%s\n", e->command);
} else {
// Format timestamp
time_t t = (time_t)e->timestamp;
struct tm *tm = localtime(&t);
char time_str[64];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm);
printf("%s | %s | %s | %dms | %s\n",
time_str, e->hostname, e->cwd, e->duration, e->command);
}
}
search_free_result(res);
}
db_close();
return 0;
}
int cmd_import(int argc, char **argv) {
if (!setup_db()) return 1;
const char *shell = "auto";
if (argc > 1) {
shell = argv[1];
}
history_import(shell);
db_close();
return 0;
}
int cmd_list(int argc, char **argv) {
if (!setup_db()) return 1;
printf("List command not implemented yet.\n");
db_close();
return 0;
}
int cmd_stats(int argc, char **argv) {
if (!setup_db()) return 1;
int limit = 10;
static struct option long_options[] = {
{"top", required_argument, 0, 't'},
{0, 0, 0, 0}
};
int opt_index = 0;
int c;
optind = 1;
while ((c = getopt_long(argc, argv, "t:", long_options, &opt_index)) != -1) {
switch (c) {
case 't': limit = atoi(optarg); break;
}
}
history_stats(limit);
db_close();
return 0;
}
int cmd_delete(int argc, char **argv) {
if (!setup_db()) return 1;
printf("Delete command not implemented yet.\n");
db_close();
return 0;
}
#include "shell_integration.h"
// ... (other includes)
int cmd_init(int argc, char **argv) {
if (argc > 1) {
if (!shell_integration_init(argv[1])) {
fprintf(stderr, "Unsupported shell: %s. Supported: bash, zsh, fish\n", argv[1]);
return 1;
}
} else {
printf("Usage: rhistory init [bash|zsh|fish]\n");
return 1;
}
return 0;
}
int cmd_config(int argc, char **argv) {
printf("Config command not implemented yet.\n");
return 0;
}
int cmd_record(int argc, char **argv) {
if (!setup_db()) return 1;
char *command = NULL;
char *cwd = NULL;
int exit_code = 0;
int duration = 0;
char *hostname = NULL;
char *session = NULL;
char *shell = NULL;
static struct option long_options[] = {
{"command", required_argument, 0, 'c'},
{"cwd", required_argument, 0, 'w'},
{"exit-code", required_argument, 0, 'e'},
{"duration", required_argument, 0, 'd'},
{"hostname", required_argument, 0, 'H'},
{"session", required_argument, 0, 's'},
{"shell", required_argument, 0, 'S'},
{0, 0, 0, 0}
};
int opt_index = 0;
int c;
while ((c = getopt_long(argc, argv, "", long_options, &opt_index)) != -1) {
switch (c) {
case 'c': command = optarg; break;
case 'w': cwd = optarg; break;
case 'e': exit_code = atoi(optarg); break;
case 'd': duration = atoi(optarg); break;
case 'H': hostname = optarg; break;
case 's': session = optarg; break;
case 'S': shell = optarg; break;
}
}
if (command && cwd) {
history_record(command, cwd, exit_code, duration, hostname, session, shell);
}
db_close();
return 0;
}