start from scratch
This commit is contained in:
13
.gdb_history
Normal file
13
.gdb_history
Normal file
@ -0,0 +1,13 @@
|
||||
r
|
||||
b exit
|
||||
r
|
||||
where
|
||||
b exit
|
||||
r
|
||||
l
|
||||
b exit
|
||||
r
|
||||
p argv[0]
|
||||
p * argv[0]
|
||||
p (char *)argv[0]
|
||||
p argc
|
15
Makefile
15
Makefile
@ -9,22 +9,23 @@ else
|
||||
CXXFLAGS += -O3 -fno-stack-protector -fno-exceptions -fno-rtti
|
||||
endif
|
||||
|
||||
LDLIBS += $$(pkgconf --cflags --libs ncurses) $$(pkgconf --cflags --libs readline)
|
||||
CXXFLAGS += -std=gnu++20 -I./source/ -I./object/ -I./
|
||||
LIBFLAGS := $$(pkgconf --cflags ncurses readline sqlite3)
|
||||
CXXFLAGS += -std=gnu++20 -I./source/ -I./object/ -I./ ${LIBFLAGS}
|
||||
LINKasd += $$(pkgconf --libs ncurses readline sqlite3)
|
||||
|
||||
OBJECT.d:=object/
|
||||
SOURCE.d:=source/
|
||||
SOURCE:=main.cpp tui.cpp db.cpp config.l
|
||||
SOURCE:=bash_history.yy.cpp main.cpp tui.cpp storage.cpp damerau_levenshtein.cpp
|
||||
OBJECT:=$(addprefix ${OBJECT.d},$(addsuffix .o,$(basename ${SOURCE})))
|
||||
SOURCE:=$(addprefix ${SOURCE.d},${SOURCE})
|
||||
OBJECT:=$(addprefix ${OBJECT.d},$(subst ${SOURCE.d},,$(addsuffix .o,$(basename ${SOURCE}))))
|
||||
|
||||
OUTPUT:=histui
|
||||
|
||||
${OUTPUT}: ${OBJECT}
|
||||
${LINK.cpp} ${OBJECT} -o ${OUTPUT} ${LDLIBS}
|
||||
${LINK.cpp} ${OBJECT} ${LINKasd} -o ${OUTPUT}
|
||||
|
||||
object/%.l.cpp: source/%.l
|
||||
${LEX} ${LFLAGS} -o $@ $<
|
||||
object/%.yy.cpp: source/%.l
|
||||
${LEX} ${LFLAGS} --prefix=$*_ --header-file=$(basename $@).hpp -o $@ $<
|
||||
|
||||
object/%.o: object/%.l.cpp
|
||||
${COMPILE.cpp} $< -o $@
|
||||
|
61
documentation/bash_history_infodump.md
Normal file
61
documentation/bash_history_infodump.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Bash history infodump
|
||||
|
||||
Bash stores its history at `$HISTFILE`.
|
||||
|
||||
It's reasonable to assume that we will only need to search `HISTSIZE`
|
||||
number of entries
|
||||
|
||||
`shopt -s lithist` promises to store multiline commands with embeded newlines,
|
||||
otherwise newlines are removed and the command is stored like that.
|
||||
However, when a new session is initialized, bash totally forgets
|
||||
which commands were the same 1 command with newlines
|
||||
|
||||
Time stamps configured with `HISTTIMEFORMAT`.
|
||||
If its set bash will save command timestamps along with the commands
|
||||
in your histfile as "comments".
|
||||
Comments entered in bash are also stored as comments.
|
||||
`HISTTIMEFORMAT` also only influences the output of the `history` builtin,
|
||||
timestamps are always stored in POSIX format.
|
||||
|
||||
## tl;dr on how bash history parsing works in practice
|
||||
+ `#<int>` is always interpreted as a POSIX timestamp
|
||||
+ a timestamp applies to all commands bellow it
|
||||
+ bash always reads timestamps
|
||||
+ invalid timestamps are silently ignored as long as you dont query them,
|
||||
in which case an error is printed
|
||||
|
||||
## Warcrimes
|
||||
Yes, all the above means that .bash\_history is fucking cursed.
|
||||
The bellow script RAPES bash in a number of ways:
|
||||
```sh
|
||||
# below me is not a comment
|
||||
#1700000000
|
||||
echo test
|
||||
HISTTIMEFORMAT='%y/%m/%d %T: '
|
||||
#1700000000
|
||||
echo test
|
||||
for i in *; do
|
||||
echo "o"
|
||||
done
|
||||
for i in *; do # harder daddy
|
||||
echo "ah"
|
||||
done
|
||||
shopt -s lithist
|
||||
for i in *; do
|
||||
echo "ah"
|
||||
done
|
||||
for i in *; do
|
||||
#1700000000
|
||||
echo "ah"
|
||||
done
|
||||
```
|
||||
|
||||
+ comment on the second line is lost as it maches the regex of a time stamp
|
||||
+ the 2nd line comment *should* insert a fake timestamp, which works
|
||||
in a true interactive shell, but if you run this whole comment it itself
|
||||
gets a forged timestamp made by bash
|
||||
+ the 3th comment can never fake a timestamp because its always timestamped
|
||||
+ the 1st for loop is stored as 1 line
|
||||
+ the 2nd for loop is stored as 2 lines because newlines are always preserved in comments
|
||||
+ the 3th for loop is 3 lines becaise of lithist, they share the timestamp
|
||||
+ the 4th for loop has an always working forged timestamp, inserted in the middle
|
14
source/bash_history.l
Normal file
14
source/bash_history.l
Normal file
@ -0,0 +1,14 @@
|
||||
%{
|
||||
#include "storage.hpp"
|
||||
long timestamp;
|
||||
%}
|
||||
%option noyywrap
|
||||
%%
|
||||
\#[[:digit:]]+ {
|
||||
timestamp = strtoll(yytext+1, NULL, 10);
|
||||
}
|
||||
[^\#\n].* {
|
||||
insert_entry(timestamp, yytext);
|
||||
}
|
||||
\n { ; }
|
||||
%%
|
42
source/cli.cpp
Normal file
42
source/cli.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
[[ noreturn ]]
|
||||
void version() {
|
||||
puts("Histui "
|
||||
#include "version.inc"
|
||||
);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
[[ noreturn ]]
|
||||
void usage(int exit_value = 0) {
|
||||
// TODO
|
||||
exit(exit_value);
|
||||
}
|
||||
|
||||
void global_options(const int argc, const char * const * const argv) {
|
||||
for(int i = 0; i < argc; i++) {
|
||||
if (not strcmp(argv[i], "-v")
|
||||
|| not strcmp(argv[i], "--version")) {
|
||||
version();
|
||||
}
|
||||
if (not strcmp(argv[i], "-h")
|
||||
|| not strcmp(argv[i], "--help")) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef signed (*mainlike_t)(int argc, char * * argv);
|
||||
map<const char*, mainlike_t> verb_table = {
|
||||
{"tui", tui_main},
|
||||
{"import", import_main},
|
||||
{"export", export_main},
|
||||
};
|
@ -1 +0,0 @@
|
||||
int read_config(const char * const file);
|
@ -1,11 +0,0 @@
|
||||
%option noyywrap
|
||||
%option nodefault
|
||||
%option noyylineno
|
||||
|
||||
%%
|
||||
|
||||
%%
|
||||
|
||||
int read_config(const char * const file) {
|
||||
return 0;
|
||||
}
|
121
source/damerau_levenshtein.cpp
Normal file
121
source/damerau_levenshtein.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
// @BAKE clang -shared -fPIC -Isqlite3 -o damerau_levenshtein.sqlext damerau_levenshtein.c
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
// helpers
|
||||
int minimum(const int a, const int b, const int c);
|
||||
|
||||
/* internal function for damerau-levenshtein distance calculation
|
||||
*/
|
||||
static int
|
||||
damerau_levenshtein_(
|
||||
int n,
|
||||
const char *const s,
|
||||
int m,
|
||||
const char *const t
|
||||
){
|
||||
//Step 1
|
||||
if ((n == 0) || (m == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int *d = (int*)malloc(sizeof(int)*(m+1)*(n+1));
|
||||
m++;
|
||||
n++;
|
||||
|
||||
//Step 2
|
||||
int k;
|
||||
for(k=0;k<n;k++) d[k]=k;
|
||||
for(k=0;k<m;k++) d[k*n]=k;
|
||||
|
||||
//Step 3 and 4
|
||||
int i, j;
|
||||
for(i=1; i<n; i++) {
|
||||
for(j=1; j<m; j++) {
|
||||
int cost;
|
||||
|
||||
//Step 5
|
||||
//cost = s[i-1] != t[j-1];
|
||||
if (s[i-1] == t[j-1]) {
|
||||
cost = 0;
|
||||
}
|
||||
else {
|
||||
cost = 1;
|
||||
}
|
||||
|
||||
//Step 6
|
||||
d[j*n+i] = minimum(d[(j-1)*n+i]+1, d[j*n+i-1]+1, d[(j-1)*n+i-1]+cost);
|
||||
|
||||
//Step 7 – only difference from pure Levenshtein - transposition
|
||||
if ( (i > 1) && (j > 1) && (s[i-1] == t[j-2]) && (s[i-2] == t[j-1]) ) {
|
||||
d[j*n+i] = minimum(INT_MAX,
|
||||
d[j*n+i],
|
||||
d[(j-2)*n+(i-2)] + cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int distance = d[n*m-1];
|
||||
free(d);
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
/*
|
||||
function to determine damerau-levenshtein distance
|
||||
damerau_levenshtein(src,dts) => int
|
||||
*/
|
||||
void
|
||||
damerau_levenshtein(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
|
||||
const char *const s = (const char *)sqlite3_value_text(argv[0]);
|
||||
const char *const t = (const char *)sqlite3_value_text(argv[1]);
|
||||
const int n = strlen(s);
|
||||
const int m = strlen(t);
|
||||
|
||||
const int distance = damerau_levenshtein_(n, s, m, t);
|
||||
|
||||
sqlite3_result_int(context, distance);
|
||||
}
|
||||
|
||||
/*
|
||||
function ensure damerau-levenshtein distance
|
||||
damerau_levenshtein(src,dts,max_distance) => bool
|
||||
*/
|
||||
void
|
||||
is_damerau_levenshtein(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
|
||||
const char *const s = (const char *)sqlite3_value_text(argv[0]);
|
||||
const char *const t = (const char *)sqlite3_value_text(argv[1]);
|
||||
const int n = strlen(s);
|
||||
const int m = strlen(t);
|
||||
const int max_distance = sqlite3_value_int(argv[2]);
|
||||
|
||||
if (abs(n - m) > max_distance) {
|
||||
sqlite3_result_int(context, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const int distance = damerau_levenshtein_(n, s, m, t);
|
||||
|
||||
sqlite3_result_int(context, distance <= max_distance);
|
||||
}
|
||||
|
||||
|
||||
inline int minimum(const int a, const int b, const int c) {
|
||||
int min = a;
|
||||
if (b < min) min=b;
|
||||
if (c < min) min=c;
|
||||
return min;
|
||||
}
|
3
source/damerau_levenshtein.hpp
Normal file
3
source/damerau_levenshtein.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
extern void damerau_levenshtein(sqlite3_context *context, int argc, sqlite3_value **argv);
|
||||
extern void is_damerau_levenshtein(sqlite3_context *context, int argc, sqlite3_value **argv);
|
@ -1,11 +0,0 @@
|
||||
#include "db.hpp"
|
||||
|
||||
inline const char * db_file;
|
||||
|
||||
signed export_main(int argc, char * * argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
signed import_main(int argc, char * * argv) {
|
||||
return 0;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Shell {
|
||||
size_t id;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
std::string line;
|
||||
time_t timestamp;
|
||||
Shell * shell;
|
||||
};
|
||||
|
||||
extern const char * db_file;
|
||||
|
||||
extern void append_entry(const Entry * const entry);
|
||||
extern std::vector<Entry> search(const char * const query);
|
||||
extern std::vector<Entry> inspect(const Entry * const entry);
|
||||
|
||||
extern signed export_main(int argc, char * * argv);
|
||||
extern signed import_main(int argc, char * * argv);
|
||||
|
@ -1,61 +1,42 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "storage.hpp"
|
||||
#include "tui.hpp"
|
||||
#include "db.hpp"
|
||||
#include "bash_history.yy.hpp"
|
||||
|
||||
using namespace std;
|
||||
// XXX
|
||||
#include <sqlite3.h>
|
||||
|
||||
[[ noreturn ]]
|
||||
void version() {
|
||||
puts("Histui "
|
||||
#include "version.inc"
|
||||
);
|
||||
|
||||
exit(0);
|
||||
void init() {
|
||||
init_storage();
|
||||
init_tui();
|
||||
}
|
||||
|
||||
[[ noreturn ]]
|
||||
void usage(int exit_value = 0) {
|
||||
;
|
||||
exit(exit_value);
|
||||
void deinit() {
|
||||
deinit_storage();
|
||||
deinit_tui();
|
||||
}
|
||||
|
||||
void global_options(const int argc, const char * const * const argv) {
|
||||
for(int i = 0; i < argc; i++) {
|
||||
if (not strcmp(argv[i], "-v")
|
||||
|| not strcmp(argv[i], "--version")) {
|
||||
version();
|
||||
signed main(int argc, char * argv[]) {
|
||||
// XXX
|
||||
extern sqlite3 * db;
|
||||
extern sqlite3_stmt * stmt;
|
||||
// TODO cli stuff
|
||||
|
||||
init();
|
||||
|
||||
bash_history_in = fopen("/home/anon/stow/.cache/.bash_history", "r");
|
||||
bash_history_lex();
|
||||
|
||||
// XXX
|
||||
while (true) {
|
||||
sqlite3_prepare_v2(db, "SELECT * FROM test WHERE DAMERAU_LEVENSHTEIN(data, 'a');", -1, &stmt, 0);
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
tui_append_back(sqlite3_column_int(stmt, 0), sqlite3_column_text(stmt, 1));
|
||||
}
|
||||
if (not strcmp(argv[i], "-h")
|
||||
|| not strcmp(argv[i], "--help")) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef signed (*mainlike_t)(int argc, char * * argv);
|
||||
map<const char*, mainlike_t> verb_table = {
|
||||
{"tui", tui_main},
|
||||
{"import", import_main},
|
||||
{"export", export_main},
|
||||
};
|
||||
|
||||
signed main(int argc, char * * argv) {
|
||||
if (argc < 2) {
|
||||
usage(2);
|
||||
sqlite3_finalize(stmt);
|
||||
tui_refresh();
|
||||
}
|
||||
|
||||
global_options(argc, argv);
|
||||
deinit();
|
||||
|
||||
for (const auto &i : verb_table) {
|
||||
if (not strcmp(argv[1], i.first)) {
|
||||
return i.second(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
37
source/storage.cpp
Normal file
37
source/storage.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "storage.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sqlite3.h>
|
||||
//#include "damerau_levenshtein.h"
|
||||
#include "damerau_levenshtein2.h"
|
||||
|
||||
sqlite3 * db;
|
||||
sqlite3_stmt * stmt;
|
||||
|
||||
int init_storage(void) {
|
||||
sqlite3_open(":memory:", &db);
|
||||
sqlite3_create_function(db, "is_damerau_levenshtein", 3, SQLITE_ANY, 0, is_damerau_levenshtein, NULL, NULL);
|
||||
sqlite3_create_function(db, "damerau_levenshtein", 2, SQLITE_ANY, 0, damerau_levenshtein, NULL, NULL);
|
||||
|
||||
static const char * sql_create_table = "CREATE TABLE test (stamp INTEGER, data TEXT);";
|
||||
sqlite3_exec(db, sql_create_table, 0, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int deinit_storage(void) {
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int insert_entry(int timestamp, const char * const command) {
|
||||
static const char * sql_insert = "INSERT INTO test (stamp, data) VALUES (?, ?);";
|
||||
sqlite3_prepare_v2(db, sql_insert, -1, &stmt, 0);
|
||||
|
||||
sqlite3_bind_int64(stmt, 1, timestamp);
|
||||
sqlite3_bind_text(stmt, 2, command, -1, SQLITE_STATIC);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return 0;
|
||||
}
|
6
source/storage.hpp
Normal file
6
source/storage.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
int init_storage(void);
|
||||
int deinit_storage(void);
|
||||
|
||||
int insert_entry(int timestamp, const char * const command);
|
@ -1,17 +1,29 @@
|
||||
#include "tui.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "db.hpp"
|
||||
WINDOW * main_window;
|
||||
WINDOW * input_window;
|
||||
|
||||
static
|
||||
char * render_entry(const Entry * const entry, const char * const format) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
signed tui_main(int argc, char * * argv) {
|
||||
read_config(NULL);
|
||||
int init_tui(void) {
|
||||
initscr();
|
||||
noecho();
|
||||
curs_set(0);
|
||||
main_window = newwin(COLS-1, LINES, 0, 0);
|
||||
box(main_window, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int deinit_tui(void) {
|
||||
endwin();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tui_append_back(int timestamp, const unsigned char * const text) {
|
||||
mvprintw(1, 1, "%d, %s\n", timestamp, text);
|
||||
}
|
||||
|
||||
void tui_refresh(void) {
|
||||
wrefresh(main_window);
|
||||
refresh();
|
||||
}
|
||||
|
@ -1 +1,7 @@
|
||||
signed tui_main(int argc, char * * argv);
|
||||
#pragma once
|
||||
|
||||
int init_tui(void);
|
||||
int deinit_tui(void);
|
||||
void tui_refresh(void);
|
||||
|
||||
void tui_append_back(int timestamp, const unsigned char * const text);
|
||||
|
@ -1 +1 @@
|
||||
"0.1"
|
||||
"0.2"
|
||||
|
Reference in New Issue
Block a user