start from scratch

This commit is contained in:
anon
2024-08-01 20:58:03 +02:00
parent 620f830d1d
commit 5cb5842fc1
17 changed files with 365 additions and 115 deletions

13
.gdb_history Normal file
View 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

View File

@ -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 $@

View 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
View 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
View 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},
};

View File

@ -1 +0,0 @@
int read_config(const char * const file);

View File

@ -1,11 +0,0 @@
%option noyywrap
%option nodefault
%option noyylineno
%%
%%
int read_config(const char * const file) {
return 0;
}

View 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;
}

View 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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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
View 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
View File

@ -0,0 +1,6 @@
#pragma once
int init_storage(void);
int deinit_storage(void);
int insert_entry(int timestamp, const char * const command);

View File

@ -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();
}

View File

@ -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);

View File

@ -1 +1 @@
"0.1"
"0.2"