chore: remove legacy libtikker static library and simplify Makefile build targets
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -pedantic -std=c11 -O2
|
||||
CFLAGS += -I. -I./include -I../third_party -fPIC
|
||||
|
||||
LIB_DIR ?= ../../build/lib
|
||||
SRC_DIR := src
|
||||
OBJ_DIR := .obj
|
||||
LIB_TARGET := $(LIB_DIR)/libtikker.a
|
||||
|
||||
SOURCES := $(wildcard $(SRC_DIR)/*.c)
|
||||
OBJECTS := $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(LIB_TARGET)
|
||||
|
||||
$(OBJ_DIR):
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
||||
@echo "Compiling $<..."
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(LIB_TARGET): $(OBJECTS) | $(LIB_DIR)
|
||||
@mkdir -p $(LIB_DIR)
|
||||
@echo "Creating static library $(LIB_TARGET)..."
|
||||
@ar rcs $@ $(OBJECTS)
|
||||
@echo "✓ libtikker.a created"
|
||||
|
||||
$(LIB_DIR):
|
||||
@mkdir -p $(LIB_DIR)
|
||||
|
||||
clean:
|
||||
@rm -rf $(OBJ_DIR)
|
||||
@rm -f $(LIB_TARGET)
|
||||
@echo "✓ libtikker cleaned"
|
||||
@@ -1,72 +0,0 @@
|
||||
#ifndef TIKKER_AGGREGATOR_H
|
||||
#define TIKKER_AGGREGATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sqlite3.h>
|
||||
#include <tikker.h>
|
||||
|
||||
typedef struct {
|
||||
char date[11];
|
||||
uint64_t total;
|
||||
} tikker_daily_entry_t;
|
||||
|
||||
typedef struct {
|
||||
char date[11];
|
||||
uint32_t hour;
|
||||
uint64_t presses;
|
||||
} tikker_hourly_entry_t;
|
||||
|
||||
typedef struct {
|
||||
char week[8];
|
||||
uint64_t total;
|
||||
} tikker_weekly_entry_t;
|
||||
|
||||
typedef enum {
|
||||
TIKKER_WEEKDAY_SUNDAY = 0,
|
||||
TIKKER_WEEKDAY_MONDAY = 1,
|
||||
TIKKER_WEEKDAY_TUESDAY = 2,
|
||||
TIKKER_WEEKDAY_WEDNESDAY = 3,
|
||||
TIKKER_WEEKDAY_THURSDAY = 4,
|
||||
TIKKER_WEEKDAY_FRIDAY = 5,
|
||||
TIKKER_WEEKDAY_SATURDAY = 6
|
||||
} tikker_weekday_t;
|
||||
|
||||
int tikker_aggregate_daily(sqlite3 *db,
|
||||
tikker_daily_entry_t **entries,
|
||||
int *count);
|
||||
|
||||
int tikker_aggregate_hourly(sqlite3 *db,
|
||||
const char *date,
|
||||
tikker_hourly_entry_t **entries,
|
||||
int *count);
|
||||
|
||||
int tikker_aggregate_weekly(sqlite3 *db,
|
||||
tikker_weekly_entry_t **entries,
|
||||
int *count);
|
||||
|
||||
int tikker_aggregate_weekday(sqlite3 *db,
|
||||
tikker_weekday_stat_t **entries,
|
||||
int *count);
|
||||
|
||||
int tikker_get_peak_hour(sqlite3 *db,
|
||||
const char *date,
|
||||
uint32_t *hour,
|
||||
uint64_t *presses);
|
||||
|
||||
int tikker_get_peak_day(sqlite3 *db,
|
||||
char *date,
|
||||
uint64_t *presses);
|
||||
|
||||
int tikker_get_daily_average(sqlite3 *db,
|
||||
uint64_t *avg_presses,
|
||||
int *num_days);
|
||||
|
||||
uint64_t tikker_calculate_total_presses(sqlite3 *db);
|
||||
uint64_t tikker_calculate_total_releases(sqlite3 *db);
|
||||
uint64_t tikker_calculate_total_repeats(sqlite3 *db);
|
||||
|
||||
void tikker_free_daily_entries(tikker_daily_entry_t *entries, int count);
|
||||
void tikker_free_hourly_entries(tikker_hourly_entry_t *entries, int count);
|
||||
void tikker_free_weekly_entries(tikker_weekly_entry_t *entries, int count);
|
||||
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef TIKKER_CONFIG_H
|
||||
#define TIKKER_CONFIG_H
|
||||
|
||||
#define TIKKER_VERSION "2.0.0-enterprise"
|
||||
#define TIKKER_VERSION_MAJOR 2
|
||||
#define TIKKER_VERSION_MINOR 0
|
||||
#define TIKKER_VERSION_PATCH 0
|
||||
|
||||
#define TIKKER_DEFAULT_DB_PATH "tikker.db"
|
||||
#define TIKKER_DEFAULT_LOGS_DIR "logs_plain"
|
||||
#define TIKKER_DEFAULT_CACHE_DIR "tikker_cache"
|
||||
#define TIKKER_DEFAULT_TAGS_DB "tags.db"
|
||||
#define TIKKER_DEFAULT_LOGS_DB "logs.db"
|
||||
|
||||
#define TIKKER_TEXT_BUFFER_INITIAL 4096
|
||||
#define TIKKER_TEXT_BUFFER_MAX (1024 * 1024 * 100)
|
||||
|
||||
#define TIKKER_MAX_KEYCODE 256
|
||||
#define TIKKER_MAX_KEY_NAME 32
|
||||
#define TIKKER_MAX_DATE_STR 11
|
||||
#define TIKKER_MAX_PATH 4096
|
||||
|
||||
#define TIKKER_WORD_MIN_LENGTH 2
|
||||
#define TIKKER_WORD_MAX_LENGTH 255
|
||||
|
||||
#define TIKKER_TOP_WORDS_DEFAULT 10
|
||||
#define TIKKER_TOP_KEYS_DEFAULT 10
|
||||
|
||||
#define TIKKER_SHIFT_KEYCODE_LSHIFT 42
|
||||
#define TIKKER_SHIFT_KEYCODE_RSHIFT 54
|
||||
|
||||
#define TIKKER_KEY_SPACE 57
|
||||
|
||||
#define TIKKER_ENABLE_PROFILING 0
|
||||
#define TIKKER_ENABLE_DEBUG 0
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef TIKKER_DATABASE_H
|
||||
#define TIKKER_DATABASE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
typedef struct {
|
||||
const char *db_path;
|
||||
sqlite3 *conn;
|
||||
int flags;
|
||||
} tikker_db_t;
|
||||
|
||||
tikker_db_t* tikker_db_open(const char *path);
|
||||
void tikker_db_close(tikker_db_t *db);
|
||||
int tikker_db_init_schema(tikker_db_t *db);
|
||||
int tikker_db_execute(tikker_db_t *db, const char *sql);
|
||||
int tikker_db_query(tikker_db_t *db, const char *sql,
|
||||
int (*callback)(void*, int, char**, char**),
|
||||
void *arg);
|
||||
int tikker_db_begin_transaction(tikker_db_t *db);
|
||||
int tikker_db_commit_transaction(tikker_db_t *db);
|
||||
int tikker_db_rollback_transaction(tikker_db_t *db);
|
||||
int tikker_db_vacuum(tikker_db_t *db);
|
||||
int tikker_db_pragma(tikker_db_t *db, const char *pragma, char *result, size_t result_size);
|
||||
int tikker_db_integrity_check(tikker_db_t *db);
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef TIKKER_DECODER_H
|
||||
#define TIKKER_DECODER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TIKKER_KEY_SPACE 57
|
||||
#define TIKKER_KEY_ENTER 28
|
||||
#define TIKKER_KEY_TAB 15
|
||||
#define TIKKER_KEY_BACKSPACE 14
|
||||
#define TIKKER_KEY_LSHIFT 42
|
||||
#define TIKKER_KEY_RSHIFT 54
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t capacity;
|
||||
size_t length;
|
||||
} tikker_text_buffer_t;
|
||||
|
||||
tikker_text_buffer_t* tikker_text_buffer_create(size_t initial_capacity);
|
||||
void tikker_text_buffer_free(tikker_text_buffer_t *buf);
|
||||
int tikker_text_buffer_append(tikker_text_buffer_t *buf, const char *data, size_t len);
|
||||
int tikker_text_buffer_append_char(tikker_text_buffer_t *buf, char c);
|
||||
void tikker_text_buffer_pop(tikker_text_buffer_t *buf);
|
||||
|
||||
int tikker_keycode_to_char(uint32_t keycode, int shift_active, char *out_char);
|
||||
const char* tikker_keycode_to_name(uint32_t keycode);
|
||||
|
||||
int tikker_decode_file(const char *input_path, const char *output_path);
|
||||
int tikker_decode_buffer(const char *input, size_t input_len,
|
||||
tikker_text_buffer_t *output);
|
||||
|
||||
#endif
|
||||
@@ -1,57 +0,0 @@
|
||||
#ifndef TIKKER_INDEXER_H
|
||||
#define TIKKER_INDEXER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
typedef struct {
|
||||
const char *word;
|
||||
uint64_t count;
|
||||
int rank;
|
||||
} tikker_word_entry_t;
|
||||
|
||||
typedef struct {
|
||||
sqlite3 *db;
|
||||
int word_count;
|
||||
uint64_t total_words;
|
||||
} tikker_word_index_t;
|
||||
|
||||
tikker_word_index_t* tikker_word_index_open(const char *db_path);
|
||||
void tikker_word_index_close(tikker_word_index_t *index);
|
||||
int tikker_word_index_reset(tikker_word_index_t *index);
|
||||
|
||||
int tikker_index_text_file(const char *file_path,
|
||||
const char *db_path);
|
||||
|
||||
int tikker_index_directory(const char *dir_path,
|
||||
const char *db_path);
|
||||
|
||||
int tikker_word_index_add(tikker_word_index_t *index,
|
||||
const char *word,
|
||||
uint64_t count);
|
||||
|
||||
int tikker_word_index_commit(tikker_word_index_t *index);
|
||||
|
||||
int tikker_word_get_frequency(const char *db_path,
|
||||
const char *word,
|
||||
uint64_t *count);
|
||||
|
||||
int tikker_word_get_rank(const char *db_path,
|
||||
const char *word,
|
||||
int *rank,
|
||||
uint64_t *count);
|
||||
|
||||
int tikker_word_get_top(const char *db_path,
|
||||
int limit,
|
||||
tikker_word_entry_t **entries,
|
||||
int *count);
|
||||
|
||||
int tikker_word_get_total_count(const char *db_path,
|
||||
uint64_t *total);
|
||||
|
||||
int tikker_word_get_unique_count(const char *db_path,
|
||||
int *count);
|
||||
|
||||
void tikker_word_entries_free(tikker_word_entry_t *entries, int count);
|
||||
|
||||
#endif
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef TIKKER_REPORT_H
|
||||
#define TIKKER_REPORT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
typedef struct {
|
||||
char *title;
|
||||
char *data;
|
||||
size_t data_size;
|
||||
} tikker_report_t;
|
||||
|
||||
int tikker_merge_text_files(const char *input_dir,
|
||||
const char *pattern,
|
||||
const char *output_path);
|
||||
|
||||
void tikker_report_free(tikker_report_t *report);
|
||||
|
||||
#endif
|
||||
@@ -1,138 +0,0 @@
|
||||
#ifndef TIKKER_H
|
||||
#define TIKKER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define TIKKER_SUCCESS 0
|
||||
#define TIKKER_ERROR_DB -1
|
||||
#define TIKKER_ERROR_MEMORY -2
|
||||
#define TIKKER_ERROR_IO -3
|
||||
#define TIKKER_ERROR_INVALID -4
|
||||
#define TIKKER_ERROR_NOT_FOUND -5
|
||||
|
||||
typedef struct {
|
||||
sqlite3 *db;
|
||||
const char *db_path;
|
||||
uint32_t flags;
|
||||
} tikker_context_t;
|
||||
|
||||
typedef struct {
|
||||
const char *word;
|
||||
uint64_t count;
|
||||
float percentage;
|
||||
} tikker_word_stat_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t keycode;
|
||||
const char *key_name;
|
||||
uint64_t count;
|
||||
} tikker_key_stat_t;
|
||||
|
||||
typedef struct {
|
||||
char date[11];
|
||||
uint64_t total_presses;
|
||||
uint64_t total_releases;
|
||||
uint64_t total_repeats;
|
||||
} tikker_daily_stat_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t hour;
|
||||
uint64_t presses;
|
||||
} tikker_hourly_stat_t;
|
||||
|
||||
typedef struct {
|
||||
char weekday[10];
|
||||
uint64_t presses;
|
||||
} tikker_weekday_stat_t;
|
||||
|
||||
typedef struct {
|
||||
double decode_time;
|
||||
double index_time;
|
||||
double aggregate_time;
|
||||
uint64_t records_processed;
|
||||
} tikker_perf_metrics_t;
|
||||
|
||||
tikker_context_t* tikker_open(const char *db_path);
|
||||
void tikker_close(tikker_context_t *ctx);
|
||||
int tikker_init_schema(tikker_context_t *ctx);
|
||||
int tikker_get_version(char *buffer, size_t size);
|
||||
|
||||
int tikker_get_daily_stats(tikker_context_t *ctx,
|
||||
tikker_daily_stat_t **stats,
|
||||
int *count);
|
||||
|
||||
int tikker_get_hourly_stats(tikker_context_t *ctx,
|
||||
const char *date,
|
||||
tikker_hourly_stat_t **stats,
|
||||
int *count);
|
||||
|
||||
int tikker_get_weekday_stats(tikker_context_t *ctx,
|
||||
tikker_weekday_stat_t **stats,
|
||||
int *count);
|
||||
|
||||
int tikker_get_top_words(tikker_context_t *ctx,
|
||||
int limit,
|
||||
tikker_word_stat_t **words,
|
||||
int *count);
|
||||
|
||||
int tikker_get_top_keys(tikker_context_t *ctx,
|
||||
int limit,
|
||||
tikker_key_stat_t **keys,
|
||||
int *count);
|
||||
|
||||
int tikker_get_date_range(tikker_context_t *ctx,
|
||||
char *min_date,
|
||||
char *max_date);
|
||||
|
||||
int tikker_get_event_counts(tikker_context_t *ctx,
|
||||
uint64_t *pressed,
|
||||
uint64_t *released,
|
||||
uint64_t *repeated);
|
||||
|
||||
int tikker_decode_keylog(const char *input_file,
|
||||
const char *output_file);
|
||||
|
||||
int tikker_decode_keylog_buffer(const char *input,
|
||||
size_t input_len,
|
||||
char **output,
|
||||
size_t *output_len);
|
||||
|
||||
int tikker_index_text_file(const char *file_path,
|
||||
const char *db_path);
|
||||
|
||||
int tikker_index_directory(const char *dir_path,
|
||||
const char *db_path);
|
||||
|
||||
int tikker_get_word_frequency(const char *db_path,
|
||||
const char *word,
|
||||
uint64_t *count);
|
||||
|
||||
int tikker_get_top_words_from_db(const char *db_path,
|
||||
int limit,
|
||||
tikker_word_stat_t **words,
|
||||
int *count);
|
||||
|
||||
int tikker_generate_html_report(tikker_context_t *ctx,
|
||||
const char *output_file,
|
||||
const char *graph_dir);
|
||||
|
||||
int tikker_generate_json_report(tikker_context_t *ctx,
|
||||
char **json_output);
|
||||
|
||||
int tikker_merge_text_files(const char *input_dir,
|
||||
const char *pattern,
|
||||
const char *output_path);
|
||||
|
||||
int tikker_get_metrics(tikker_perf_metrics_t *metrics);
|
||||
|
||||
void tikker_free_words(tikker_word_stat_t *words, int count);
|
||||
void tikker_free_keys(tikker_key_stat_t *keys, int count);
|
||||
void tikker_free_daily_stats(tikker_daily_stat_t *stats, int count);
|
||||
void tikker_free_hourly_stats(tikker_hourly_stat_t *stats, int count);
|
||||
void tikker_free_weekday_stats(tikker_weekday_stat_t *stats, int count);
|
||||
void tikker_free_json(char *json);
|
||||
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
#ifndef TIKKER_TYPES_H
|
||||
#define TIKKER_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef enum {
|
||||
TIKKER_LOG_DEBUG = 0,
|
||||
TIKKER_LOG_INFO = 1,
|
||||
TIKKER_LOG_WARN = 2,
|
||||
TIKKER_LOG_ERROR = 3,
|
||||
TIKKER_LOG_FATAL = 4
|
||||
} tikker_log_level_t;
|
||||
|
||||
typedef enum {
|
||||
TIKKER_EVENT_PRESSED = 0,
|
||||
TIKKER_EVENT_RELEASED = 1,
|
||||
TIKKER_EVENT_REPEATED = 2
|
||||
} tikker_event_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t id;
|
||||
uint32_t keycode;
|
||||
tikker_event_type_t event;
|
||||
const char *name;
|
||||
time_t timestamp;
|
||||
char character;
|
||||
} tikker_kevent_t;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *symbol;
|
||||
uint32_t code;
|
||||
} tikker_key_mapping_t;
|
||||
|
||||
typedef struct {
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
int weekday;
|
||||
} tikker_datetime_t;
|
||||
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
size_t capacity;
|
||||
size_t length;
|
||||
} tikker_string_t;
|
||||
|
||||
tikker_string_t* tikker_string_create(size_t capacity);
|
||||
void tikker_string_free(tikker_string_t *str);
|
||||
int tikker_string_append(tikker_string_t *str, const char *data);
|
||||
int tikker_string_append_char(tikker_string_t *str, char c);
|
||||
void tikker_string_clear(tikker_string_t *str);
|
||||
char* tikker_string_cstr(tikker_string_t *str);
|
||||
|
||||
#endif
|
||||
@@ -1,92 +0,0 @@
|
||||
#include <aggregator.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
int tikker_aggregate_daily(sqlite3 *db,
|
||||
tikker_daily_entry_t **entries,
|
||||
int *count) {
|
||||
if (!db || !entries || !count) return -1;
|
||||
*entries = NULL;
|
||||
*count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_aggregate_hourly(sqlite3 *db,
|
||||
const char *date,
|
||||
tikker_hourly_entry_t **entries,
|
||||
int *count) {
|
||||
if (!db || !date || !entries || !count) return -1;
|
||||
*entries = NULL;
|
||||
*count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_aggregate_weekly(sqlite3 *db,
|
||||
tikker_weekly_entry_t **entries,
|
||||
int *count) {
|
||||
if (!db || !entries || !count) return -1;
|
||||
*entries = NULL;
|
||||
*count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_aggregate_weekday(sqlite3 *db,
|
||||
tikker_weekday_stat_t **entries,
|
||||
int *count) {
|
||||
if (!db || !entries || !count) return -1;
|
||||
*entries = NULL;
|
||||
*count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_get_peak_hour(sqlite3 *db,
|
||||
const char *date,
|
||||
uint32_t *hour,
|
||||
uint64_t *presses) {
|
||||
if (!db || !date || !hour || !presses) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_get_peak_day(sqlite3 *db,
|
||||
char *date,
|
||||
uint64_t *presses) {
|
||||
if (!db || !date || !presses) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_get_daily_average(sqlite3 *db,
|
||||
uint64_t *avg_presses,
|
||||
int *num_days) {
|
||||
if (!db || !avg_presses || !num_days) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t tikker_calculate_total_presses(sqlite3 *db) {
|
||||
if (!db) return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t tikker_calculate_total_releases(sqlite3 *db) {
|
||||
if (!db) return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t tikker_calculate_total_repeats(sqlite3 *db) {
|
||||
if (!db) return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tikker_free_daily_entries(tikker_daily_entry_t *entries, int count) {
|
||||
if (entries) free(entries);
|
||||
}
|
||||
|
||||
void tikker_free_hourly_entries(tikker_hourly_entry_t *entries, int count) {
|
||||
if (entries) free(entries);
|
||||
}
|
||||
|
||||
void tikker_free_weekly_entries(tikker_weekly_entry_t *entries, int count) {
|
||||
if (entries) free(entries);
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
#include <database.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
tikker_db_t* tikker_db_open(const char *path) {
|
||||
if (!path) return NULL;
|
||||
tikker_db_t *db = malloc(sizeof(tikker_db_t));
|
||||
if (!db) return NULL;
|
||||
db->db_path = path;
|
||||
db->flags = 0;
|
||||
int ret = sqlite3_open(path, &db->conn);
|
||||
if (ret != SQLITE_OK) { free(db); return NULL; }
|
||||
return db;
|
||||
}
|
||||
|
||||
void tikker_db_close(tikker_db_t *db) {
|
||||
if (!db) return;
|
||||
if (db->conn) sqlite3_close(db->conn);
|
||||
free(db);
|
||||
}
|
||||
|
||||
int tikker_db_init_schema(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_db_execute(tikker_db_t *db, const char *sql) {
|
||||
if (!db || !db->conn || !sql) return -1;
|
||||
char *errmsg = NULL;
|
||||
int ret = sqlite3_exec(db->conn, sql, NULL, NULL, &errmsg);
|
||||
if (errmsg) sqlite3_free(errmsg);
|
||||
return ret == SQLITE_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int tikker_db_query(tikker_db_t *db, const char *sql,
|
||||
int (*callback)(void*, int, char**, char**),
|
||||
void *arg) {
|
||||
if (!db || !db->conn || !sql) return -1;
|
||||
char *errmsg = NULL;
|
||||
int ret = sqlite3_exec(db->conn, sql, callback, arg, &errmsg);
|
||||
if (errmsg) sqlite3_free(errmsg);
|
||||
return ret == SQLITE_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int tikker_db_begin_transaction(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return tikker_db_execute(db, "BEGIN TRANSACTION;");
|
||||
}
|
||||
|
||||
int tikker_db_commit_transaction(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return tikker_db_execute(db, "COMMIT;");
|
||||
}
|
||||
|
||||
int tikker_db_rollback_transaction(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return tikker_db_execute(db, "ROLLBACK;");
|
||||
}
|
||||
|
||||
int tikker_db_vacuum(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return tikker_db_execute(db, "VACUUM;");
|
||||
}
|
||||
|
||||
int tikker_db_pragma(tikker_db_t *db, const char *pragma, char *result, size_t result_size) {
|
||||
if (!db || !db->conn || !pragma || !result) return -1;
|
||||
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql), "PRAGMA %s", pragma);
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const unsigned char *text = sqlite3_column_text(stmt, 0);
|
||||
if (text) {
|
||||
snprintf(result, result_size, "%s", (const char *)text);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_db_integrity_check(tikker_db_t *db) {
|
||||
if (!db || !db->conn) return -1;
|
||||
return tikker_db_execute(db, "PRAGMA integrity_check;");
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
#include <decoder.h>
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *keycode_names[] = {
|
||||
"NONE", "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=",
|
||||
"BACKSPACE", "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
|
||||
"[", "]", "ENTER", "L_CTRL", "A", "S", "D", "F", "G", "H", "J", "K",
|
||||
"L", ";", "'", "`", "L_SHIFT", "\\", "Z", "X", "C", "V", "B", "N", "M",
|
||||
",", ".", "/", "R_SHIFT", "*", "L_ALT", "SPACE", "CAPSLOCK"
|
||||
};
|
||||
|
||||
tikker_text_buffer_t* tikker_text_buffer_create(size_t initial_capacity) {
|
||||
tikker_text_buffer_t *buf = malloc(sizeof(tikker_text_buffer_t));
|
||||
if (!buf) return NULL;
|
||||
buf->capacity = initial_capacity ? initial_capacity : 4096;
|
||||
buf->data = malloc(buf->capacity);
|
||||
if (!buf->data) { free(buf); return NULL; }
|
||||
buf->length = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void tikker_text_buffer_free(tikker_text_buffer_t *buf) {
|
||||
if (!buf) return;
|
||||
if (buf->data) free(buf->data);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int tikker_text_buffer_append(tikker_text_buffer_t *buf, const char *data, size_t len) {
|
||||
if (!buf || !data) return -1;
|
||||
if (buf->length + len >= buf->capacity) {
|
||||
size_t new_capacity = buf->capacity * 2;
|
||||
while (new_capacity < buf->length + len + 1) new_capacity *= 2;
|
||||
char *new_data = realloc(buf->data, new_capacity);
|
||||
if (!new_data) return -1;
|
||||
buf->data = new_data;
|
||||
buf->capacity = new_capacity;
|
||||
}
|
||||
memcpy(buf->data + buf->length, data, len);
|
||||
buf->length += len;
|
||||
buf->data[buf->length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_text_buffer_append_char(tikker_text_buffer_t *buf, char c) {
|
||||
return tikker_text_buffer_append(buf, &c, 1);
|
||||
}
|
||||
|
||||
void tikker_text_buffer_pop(tikker_text_buffer_t *buf) {
|
||||
if (!buf || buf->length == 0) return;
|
||||
buf->length--;
|
||||
buf->data[buf->length] = '\0';
|
||||
}
|
||||
|
||||
int tikker_keycode_to_char(uint32_t keycode, int shift_active, char *out_char) {
|
||||
if (!out_char) return -1;
|
||||
if (keycode >= 2 && keycode <= 11) {
|
||||
char base = '0' + (keycode - 2);
|
||||
if (shift_active) {
|
||||
const char *shifted[] = {"!", "@", "#", "$", "%", "^", "&", "*", "(", ")"};
|
||||
*out_char = shifted[keycode - 2][0];
|
||||
} else {
|
||||
*out_char = base;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (keycode >= 16 && keycode <= 25) {
|
||||
*out_char = 'a' + (keycode - 16);
|
||||
if (shift_active) *out_char = (*out_char) - 32;
|
||||
return 0;
|
||||
}
|
||||
if (keycode == TIKKER_KEY_SPACE) { *out_char = ' '; return 0; }
|
||||
*out_char = '?';
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* tikker_keycode_to_name(uint32_t keycode) {
|
||||
if (keycode < sizeof(keycode_names) / sizeof(keycode_names[0])) return keycode_names[keycode];
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char* shift_number_map[] = {
|
||||
"!", "@", "#", "$", "%", "^", "&", "*", "(", ")"
|
||||
};
|
||||
|
||||
int tikker_decode_buffer(const char *input, size_t input_len,
|
||||
tikker_text_buffer_t *output) {
|
||||
if (!input || !output) return -1;
|
||||
|
||||
int shift_active = 0;
|
||||
size_t i = 0;
|
||||
|
||||
while (i < input_len) {
|
||||
if (input[i] == '[') {
|
||||
size_t j = i + 1;
|
||||
while (j < input_len && input[j] != ']') j++;
|
||||
|
||||
if (j >= input_len) return -1;
|
||||
|
||||
size_t token_len = j - i - 1;
|
||||
char token[256];
|
||||
if (token_len >= sizeof(token)) return -1;
|
||||
|
||||
memcpy(token, input + i + 1, token_len);
|
||||
token[token_len] = '\0';
|
||||
|
||||
if (strcmp(token, "LEFT_SHIFT") == 0 || strcmp(token, "R_SHIFT") == 0) {
|
||||
shift_active = 1;
|
||||
} else if (strcmp(token, "BACKSPACE") == 0) {
|
||||
tikker_text_buffer_pop(output);
|
||||
} else if (strcmp(token, "TAB") == 0) {
|
||||
tikker_text_buffer_append_char(output, '\t');
|
||||
} else if (strcmp(token, "ENTER") == 0) {
|
||||
tikker_text_buffer_append_char(output, '\n');
|
||||
} else if (strcmp(token, "UP") == 0 || strcmp(token, "DOWN") == 0 ||
|
||||
strcmp(token, "LEFT") == 0 || strcmp(token, "RIGHT") == 0) {
|
||||
} else if (token_len == 1) {
|
||||
char c = token[0];
|
||||
if (shift_active) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c = c - 32;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
c = shift_number_map[c - '0'][0];
|
||||
}
|
||||
shift_active = 0;
|
||||
} else {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
c = c + 32;
|
||||
}
|
||||
}
|
||||
tikker_text_buffer_append_char(output, c);
|
||||
}
|
||||
|
||||
i = j + 1;
|
||||
} else if (input[i] == ' ' || input[i] == '\t' || input[i] == '\n') {
|
||||
i++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_decode_file(const char *input_path, const char *output_path) {
|
||||
if (!input_path || !output_path) return -1;
|
||||
|
||||
FILE *input_file = fopen(input_path, "r");
|
||||
if (!input_file) return -1;
|
||||
|
||||
fseek(input_file, 0, SEEK_END);
|
||||
long file_size = ftell(input_file);
|
||||
fseek(input_file, 0, SEEK_SET);
|
||||
|
||||
if (file_size <= 0) {
|
||||
fclose(input_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *buffer = malloc(file_size);
|
||||
if (!buffer) {
|
||||
fclose(input_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t read_bytes = fread(buffer, 1, file_size, input_file);
|
||||
fclose(input_file);
|
||||
|
||||
if (read_bytes != (size_t)file_size) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tikker_text_buffer_t *output_buf = tikker_text_buffer_create(file_size);
|
||||
if (!output_buf) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = tikker_decode_buffer(buffer, file_size, output_buf);
|
||||
free(buffer);
|
||||
|
||||
if (ret != 0) {
|
||||
tikker_text_buffer_free(output_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *output_file = fopen(output_path, "w");
|
||||
if (!output_file) {
|
||||
tikker_text_buffer_free(output_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fwrite(output_buf->data, 1, output_buf->length, output_file);
|
||||
fclose(output_file);
|
||||
|
||||
tikker_text_buffer_free(output_buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <indexer.h>
|
||||
#include <database.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
||||
tikker_word_index_t* tikker_word_index_open(const char *db_path) {
|
||||
if (!db_path) return NULL;
|
||||
tikker_word_index_t *index = malloc(sizeof(tikker_word_index_t));
|
||||
if (!index) return NULL;
|
||||
int ret = sqlite3_open(db_path, &index->db);
|
||||
if (ret != SQLITE_OK) { free(index); return NULL; }
|
||||
index->word_count = 0;
|
||||
index->total_words = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
void tikker_word_index_close(tikker_word_index_t *index) {
|
||||
if (!index) return;
|
||||
if (index->db) sqlite3_close(index->db);
|
||||
free(index);
|
||||
}
|
||||
|
||||
static int is_valid_word_char(char c) {
|
||||
return isalnum(c) || c == '_';
|
||||
}
|
||||
|
||||
int tikker_word_index_reset(tikker_word_index_t *index) {
|
||||
if (!index || !index->db) return -1;
|
||||
|
||||
sqlite3_exec(index->db, "DROP TABLE IF EXISTS words", NULL, NULL, NULL);
|
||||
|
||||
const char *sql = "CREATE TABLE IF NOT EXISTS words ("
|
||||
"word TEXT NOT NULL PRIMARY KEY,"
|
||||
"count INTEGER NOT NULL)";
|
||||
|
||||
char *errmsg = NULL;
|
||||
int ret = sqlite3_exec(index->db, sql, NULL, NULL, &errmsg);
|
||||
if (errmsg) sqlite3_free(errmsg);
|
||||
|
||||
if (ret == SQLITE_OK) {
|
||||
index->word_count = 0;
|
||||
index->total_words = 0;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_index_text_file(const char *file_path, const char *db_path) {
|
||||
if (!file_path || !db_path) return -1;
|
||||
|
||||
FILE *f = fopen(file_path, "r");
|
||||
if (!f) return -1;
|
||||
|
||||
tikker_word_index_t *index = tikker_word_index_open(db_path);
|
||||
if (!index) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char word[256];
|
||||
int word_len = 0;
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(f)) != EOF) {
|
||||
if (is_valid_word_char(c)) {
|
||||
if (word_len < (int)sizeof(word) - 1) {
|
||||
word[word_len++] = tolower(c);
|
||||
}
|
||||
} else {
|
||||
if (word_len > 0) {
|
||||
word[word_len] = '\0';
|
||||
tikker_word_index_add(index, word, 1);
|
||||
word_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (word_len > 0) {
|
||||
word[word_len] = '\0';
|
||||
tikker_word_index_add(index, word, 1);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
tikker_word_index_commit(index);
|
||||
tikker_word_index_close(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_index_directory(const char *dir_path, const char *db_path) {
|
||||
if (!dir_path || !db_path) return -1;
|
||||
|
||||
DIR *dir = opendir(dir_path);
|
||||
if (!dir) return -1;
|
||||
|
||||
tikker_word_index_t *index = tikker_word_index_open(db_path);
|
||||
if (!index) {
|
||||
closedir(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_type == DT_REG && strstr(entry->d_name, ".txt")) {
|
||||
char file_path[1024];
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name);
|
||||
|
||||
FILE *f = fopen(file_path, "r");
|
||||
if (f) {
|
||||
char word[256];
|
||||
int word_len = 0;
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(f)) != EOF) {
|
||||
if (is_valid_word_char(c)) {
|
||||
if (word_len < (int)sizeof(word) - 1) {
|
||||
word[word_len++] = tolower(c);
|
||||
}
|
||||
} else {
|
||||
if (word_len >= 2) {
|
||||
word[word_len] = '\0';
|
||||
tikker_word_index_add(index, word, 1);
|
||||
}
|
||||
word_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (word_len >= 2) {
|
||||
word[word_len] = '\0';
|
||||
tikker_word_index_add(index, word, 1);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
tikker_word_index_commit(index);
|
||||
tikker_word_index_close(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_word_index_add(tikker_word_index_t *index, const char *word, uint64_t count) {
|
||||
if (!index || !index->db || !word) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "INSERT OR IGNORE INTO words (word, count) VALUES (?, 0); "
|
||||
"UPDATE words SET count = count + ? WHERE word = ?";
|
||||
|
||||
if (sqlite3_prepare_v2(index->db, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, word, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 2, count);
|
||||
sqlite3_bind_text(stmt, 3, word, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_DONE) {
|
||||
index->word_count++;
|
||||
index->total_words += count;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_word_index_commit(tikker_word_index_t *index) {
|
||||
if (!index || !index->db) return -1;
|
||||
|
||||
char *errmsg = NULL;
|
||||
int ret = sqlite3_exec(index->db, "COMMIT", NULL, NULL, &errmsg);
|
||||
if (errmsg) sqlite3_free(errmsg);
|
||||
|
||||
return ret == SQLITE_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
int tikker_word_get_frequency(const char *db_path, const char *word, uint64_t *count) {
|
||||
if (!db_path || !word || !count) return -1;
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(db_path, &db) != SQLITE_OK) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "SELECT count FROM words WHERE word = ?";
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, word, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
*count = sqlite3_column_int64(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_word_get_rank(const char *db_path, const char *word, int *rank, uint64_t *count) {
|
||||
if (!db_path || !word || !rank || !count) return -1;
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(db_path, &db) != SQLITE_OK) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "SELECT COUNT(*) + 1 FROM words WHERE count > (SELECT count FROM words WHERE word = ?)";
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, word, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
*rank = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
tikker_word_get_frequency(db_path, word, count);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_word_get_top(const char *db_path, int limit, tikker_word_entry_t **entries, int *count) {
|
||||
if (!db_path || limit <= 0 || !entries || !count) return -1;
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(db_path, &db) != SQLITE_OK) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "SELECT word, count FROM words ORDER BY count DESC LIMIT ?";
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_bind_int(stmt, 1, limit);
|
||||
|
||||
int result_count = 0;
|
||||
tikker_word_entry_t *result = malloc(limit * sizeof(tikker_word_entry_t));
|
||||
if (!result) {
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW && result_count < limit) {
|
||||
const char *word_str = (const char *)sqlite3_column_text(stmt, 0);
|
||||
uint64_t word_count = sqlite3_column_int64(stmt, 1);
|
||||
|
||||
result[result_count].word = strdup(word_str);
|
||||
result[result_count].count = word_count;
|
||||
result[result_count].rank = result_count + 1;
|
||||
result_count++;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
|
||||
*entries = result;
|
||||
*count = result_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_word_get_total_count(const char *db_path, uint64_t *total) {
|
||||
if (!db_path || !total) return -1;
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(db_path, &db) != SQLITE_OK) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "SELECT SUM(count) FROM words";
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
*total = sqlite3_column_int64(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
*total = 0;
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tikker_word_get_unique_count(const char *db_path, int *count) {
|
||||
if (!db_path || !count) return -1;
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(db_path, &db) != SQLITE_OK) return -1;
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
const char *sql = "SELECT COUNT(*) FROM words";
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
*count = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void tikker_word_entries_free(tikker_word_entry_t *entries, int count) {
|
||||
if (entries) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (entries[i].word) free((char *)entries[i].word);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <report.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
|
||||
int tikker_merge_text_files(const char *input_dir, const char *pattern, const char *output_path) {
|
||||
if (!input_dir || !output_path) return -1;
|
||||
|
||||
DIR *dir = opendir(input_dir);
|
||||
if (!dir) return -1;
|
||||
|
||||
FILE *output_file = fopen(output_path, "w");
|
||||
if (!output_file) {
|
||||
closedir(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
int first = 1;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_type == DT_REG && strstr(entry->d_name, ".txt")) {
|
||||
if (strcmp(entry->d_name, "merged.txt") == 0) continue;
|
||||
|
||||
char file_path[1024];
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s", input_dir, entry->d_name);
|
||||
|
||||
FILE *input_file = fopen(file_path, "r");
|
||||
if (input_file) {
|
||||
if (!first) {
|
||||
fprintf(output_file, "\n\n");
|
||||
}
|
||||
first = 0;
|
||||
|
||||
char buffer[4096];
|
||||
size_t bytes_read;
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), input_file)) > 0) {
|
||||
fwrite(buffer, 1, bytes_read, output_file);
|
||||
}
|
||||
|
||||
fclose(input_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
fclose(output_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_token_in_file(const char *file_path, const char *token) {
|
||||
FILE *f = fopen(file_path, "r");
|
||||
if (!f) return 0;
|
||||
|
||||
int count = 0;
|
||||
char buffer[4096];
|
||||
size_t bytes_read;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) > 0) {
|
||||
for (size_t i = 0; i < bytes_read; ) {
|
||||
if (buffer[i] == '[') {
|
||||
size_t j = i + 1;
|
||||
while (j < bytes_read && buffer[j] != ']') j++;
|
||||
if (j < bytes_read && strcmp(token, "ENTER") == 0) {
|
||||
if (j - i - 1 == 5 && strncmp(buffer + i + 1, "ENTER", 5) == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
i = j + 1;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int internal_generate_html(sqlite3 *db, const char *output_file, const char *title) {
|
||||
if (!db || !output_file) return -1;
|
||||
|
||||
FILE *f = fopen(output_file, "w");
|
||||
if (!f) return -1;
|
||||
|
||||
fprintf(f, "<html>\n");
|
||||
fprintf(f, "<style>\n");
|
||||
fprintf(f, " body { width:100%%; background-color: #000; color: #fff; font-family: monospace; }\n");
|
||||
fprintf(f, " img { width:40%%; padding: 4%%; float:left; }\n");
|
||||
fprintf(f, " .stats { clear: both; padding: 20px; }\n");
|
||||
fprintf(f, "</style>\n");
|
||||
fprintf(f, "<body>\n");
|
||||
|
||||
if (title) {
|
||||
fprintf(f, "<h1>%s</h1>\n", title);
|
||||
}
|
||||
|
||||
fprintf(f, "<div class=\"stats\">\n");
|
||||
fprintf(f, "<p>Report generated by Tikker</p>\n");
|
||||
fprintf(f, "</div>\n");
|
||||
|
||||
fprintf(f, "</body>\n");
|
||||
fprintf(f, "</html>\n");
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int internal_generate_json(sqlite3 *db, char **json_output) {
|
||||
if (!db || !json_output) return -1;
|
||||
|
||||
size_t buffer_size = 8192;
|
||||
char *buffer = malloc(buffer_size);
|
||||
if (!buffer) return -1;
|
||||
|
||||
snprintf(buffer, buffer_size, "{\"status\":\"success\",\"timestamp\":%ld}", (long)time(NULL));
|
||||
|
||||
*json_output = buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int internal_generate_summary(sqlite3 *db, char *buffer, size_t buffer_size) {
|
||||
if (!db || !buffer || buffer_size == 0) return -1;
|
||||
|
||||
snprintf(buffer, buffer_size,
|
||||
"Tikker Statistics Summary\n"
|
||||
"========================\n"
|
||||
"Database: %s\n"
|
||||
"Generated: %s\n",
|
||||
"tikker.db", __DATE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tikker_report_free(tikker_report_t *report) {
|
||||
if (!report) return;
|
||||
if (report->title) free(report->title);
|
||||
if (report->data) free(report->data);
|
||||
free(report);
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
#include <tikker.h>
|
||||
#include <config.h>
|
||||
#include <decoder.h>
|
||||
#include <indexer.h>
|
||||
#include <report.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
tikker_context_t* tikker_open(const char *db_path) {
|
||||
tikker_context_t *ctx = malloc(sizeof(tikker_context_t));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
ctx->db_path = db_path ? strdup(db_path) : strdup(TIKKER_DEFAULT_DB_PATH);
|
||||
ctx->flags = 0;
|
||||
|
||||
int ret = sqlite3_open(ctx->db_path, &ctx->db);
|
||||
if (ret != SQLITE_OK) {
|
||||
free((void*)ctx->db_path);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void tikker_close(tikker_context_t *ctx) {
|
||||
if (!ctx) return;
|
||||
if (ctx->db) sqlite3_close(ctx->db);
|
||||
if (ctx->db_path) free((void*)ctx->db_path);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int tikker_init_schema(tikker_context_t *ctx) {
|
||||
if (!ctx || !ctx->db) return TIKKER_ERROR_DB;
|
||||
|
||||
const char *schema = "CREATE TABLE IF NOT EXISTS kevent ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"code INTEGER,"
|
||||
"event TEXT,"
|
||||
"name TEXT,"
|
||||
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,"
|
||||
"char TEXT"
|
||||
");"
|
||||
"CREATE INDEX IF NOT EXISTS idx_kevent_event ON kevent(event);"
|
||||
"CREATE VIEW IF NOT EXISTS presses_per_hour AS "
|
||||
"SELECT COUNT(0) as press_count, "
|
||||
"(SELECT COUNT(0) FROM kevent) as total, "
|
||||
"strftime('%Y-%m-%d.%H', timestamp) as period "
|
||||
"FROM kevent WHERE event='PRESSED' GROUP BY period;";
|
||||
|
||||
char *errmsg = NULL;
|
||||
int ret = sqlite3_exec(ctx->db, schema, NULL, NULL, &errmsg);
|
||||
if (ret != SQLITE_OK) {
|
||||
if (errmsg) sqlite3_free(errmsg);
|
||||
return TIKKER_ERROR_DB;
|
||||
}
|
||||
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_version(char *buffer, size_t size) {
|
||||
if (!buffer || size < strlen(TIKKER_VERSION) + 1) {
|
||||
return TIKKER_ERROR_INVALID;
|
||||
}
|
||||
strncpy(buffer, TIKKER_VERSION, size - 1);
|
||||
buffer[size - 1] = '\0';
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_daily_stats(tikker_context_t *ctx,
|
||||
tikker_daily_stat_t **stats,
|
||||
int *count) {
|
||||
if (!ctx || !stats || !count) return TIKKER_ERROR_INVALID;
|
||||
*stats = NULL;
|
||||
*count = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_hourly_stats(tikker_context_t *ctx,
|
||||
const char *date,
|
||||
tikker_hourly_stat_t **stats,
|
||||
int *count) {
|
||||
if (!ctx || !date || !stats || !count) return TIKKER_ERROR_INVALID;
|
||||
*stats = NULL;
|
||||
*count = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_weekday_stats(tikker_context_t *ctx,
|
||||
tikker_weekday_stat_t **stats,
|
||||
int *count) {
|
||||
if (!ctx || !stats || !count) return TIKKER_ERROR_INVALID;
|
||||
*stats = NULL;
|
||||
*count = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_top_words(tikker_context_t *ctx,
|
||||
int limit,
|
||||
tikker_word_stat_t **words,
|
||||
int *count) {
|
||||
if (!ctx || limit <= 0 || !words || !count) return TIKKER_ERROR_INVALID;
|
||||
*words = NULL;
|
||||
*count = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_top_keys(tikker_context_t *ctx,
|
||||
int limit,
|
||||
tikker_key_stat_t **keys,
|
||||
int *count) {
|
||||
if (!ctx || limit <= 0 || !keys || !count) return TIKKER_ERROR_INVALID;
|
||||
*keys = NULL;
|
||||
*count = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_date_range(tikker_context_t *ctx,
|
||||
char *min_date,
|
||||
char *max_date) {
|
||||
if (!ctx || !min_date || !max_date) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_event_counts(tikker_context_t *ctx,
|
||||
uint64_t *pressed,
|
||||
uint64_t *released,
|
||||
uint64_t *repeated) {
|
||||
if (!ctx || !pressed || !released || !repeated) return TIKKER_ERROR_INVALID;
|
||||
*pressed = 0;
|
||||
*released = 0;
|
||||
*repeated = 0;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_decode_keylog(const char *input_file,
|
||||
const char *output_file) {
|
||||
if (!input_file || !output_file) return TIKKER_ERROR_INVALID;
|
||||
if (tikker_decode_file(input_file, output_file) != 0) {
|
||||
return TIKKER_ERROR_IO;
|
||||
}
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_decode_keylog_buffer(const char *input,
|
||||
size_t input_len,
|
||||
char **output,
|
||||
size_t *output_len) {
|
||||
if (!input || !output || !output_len) return TIKKER_ERROR_INVALID;
|
||||
|
||||
tikker_text_buffer_t *buf = tikker_text_buffer_create(input_len);
|
||||
if (!buf) return TIKKER_ERROR_MEMORY;
|
||||
|
||||
if (tikker_decode_buffer(input, input_len, buf) != 0) {
|
||||
tikker_text_buffer_free(buf);
|
||||
return TIKKER_ERROR_IO;
|
||||
}
|
||||
|
||||
*output = malloc(buf->length + 1);
|
||||
if (!*output) {
|
||||
tikker_text_buffer_free(buf);
|
||||
return TIKKER_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(*output, buf->data, buf->length + 1);
|
||||
*output_len = buf->length;
|
||||
|
||||
tikker_text_buffer_free(buf);
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_index_text_file(const char *file_path,
|
||||
const char *db_path) {
|
||||
if (!file_path || !db_path) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_index_directory(const char *dir_path,
|
||||
const char *db_path) {
|
||||
if (!dir_path || !db_path) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_word_frequency(const char *db_path,
|
||||
const char *word,
|
||||
uint64_t *count) {
|
||||
if (!db_path || !word || !count) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_top_words_from_db(const char *db_path,
|
||||
int limit,
|
||||
tikker_word_stat_t **words,
|
||||
int *count) {
|
||||
if (!db_path || limit <= 0 || !words || !count) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_generate_html_report(tikker_context_t *ctx,
|
||||
const char *output_file,
|
||||
const char *graph_dir) {
|
||||
if (!ctx || !output_file) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_generate_json_report(tikker_context_t *ctx,
|
||||
char **json_output) {
|
||||
if (!ctx || !json_output) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_merge_text_files(const char *input_dir,
|
||||
const char *pattern,
|
||||
const char *output_path) {
|
||||
if (!input_dir || !output_path) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
int tikker_get_metrics(tikker_perf_metrics_t *metrics) {
|
||||
if (!metrics) return TIKKER_ERROR_INVALID;
|
||||
return TIKKER_SUCCESS;
|
||||
}
|
||||
|
||||
void tikker_free_words(tikker_word_stat_t *words, int count) {
|
||||
if (words) free(words);
|
||||
}
|
||||
|
||||
void tikker_free_keys(tikker_key_stat_t *keys, int count) {
|
||||
if (keys) free(keys);
|
||||
}
|
||||
|
||||
void tikker_free_daily_stats(tikker_daily_stat_t *stats, int count) {
|
||||
if (stats) free(stats);
|
||||
}
|
||||
|
||||
void tikker_free_hourly_stats(tikker_hourly_stat_t *stats, int count) {
|
||||
if (stats) free(stats);
|
||||
}
|
||||
|
||||
void tikker_free_weekday_stats(tikker_weekday_stat_t *stats, int count) {
|
||||
if (stats) free(stats);
|
||||
}
|
||||
|
||||
void tikker_free_json(char *json) {
|
||||
if (json) free(json);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#include <types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
tikker_string_t* tikker_string_create(size_t capacity) {
|
||||
tikker_string_t *str = malloc(sizeof(tikker_string_t));
|
||||
if (!str) return NULL;
|
||||
str->capacity = capacity ? capacity : 256;
|
||||
str->buffer = malloc(str->capacity);
|
||||
if (!str->buffer) { free(str); return NULL; }
|
||||
str->length = 0;
|
||||
str->buffer[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
void tikker_string_free(tikker_string_t *str) {
|
||||
if (!str) return;
|
||||
if (str->buffer) free(str->buffer);
|
||||
free(str);
|
||||
}
|
||||
|
||||
int tikker_string_append(tikker_string_t *str, const char *data) {
|
||||
if (!str || !data) return -1;
|
||||
size_t data_len = strlen(data);
|
||||
if (str->length + data_len >= str->capacity) {
|
||||
size_t new_capacity = str->capacity * 2;
|
||||
while (new_capacity <= str->length + data_len) new_capacity *= 2;
|
||||
char *new_buffer = realloc(str->buffer, new_capacity);
|
||||
if (!new_buffer) return -1;
|
||||
str->buffer = new_buffer;
|
||||
str->capacity = new_capacity;
|
||||
}
|
||||
strcpy(str->buffer + str->length, data);
|
||||
str->length += data_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tikker_string_append_char(tikker_string_t *str, char c) {
|
||||
if (!str) return -1;
|
||||
if (str->length + 1 >= str->capacity) {
|
||||
size_t new_capacity = str->capacity * 2;
|
||||
char *new_buffer = realloc(str->buffer, new_capacity);
|
||||
if (!new_buffer) return -1;
|
||||
str->buffer = new_buffer;
|
||||
str->capacity = new_capacity;
|
||||
}
|
||||
str->buffer[str->length] = c;
|
||||
str->length++;
|
||||
str->buffer[str->length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tikker_string_clear(tikker_string_t *str) {
|
||||
if (!str) return;
|
||||
str->length = 0;
|
||||
str->buffer[0] = '\0';
|
||||
}
|
||||
|
||||
char* tikker_string_cstr(tikker_string_t *str) {
|
||||
if (!str) return NULL;
|
||||
return str->buffer;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -pedantic -std=c11 -O2
|
||||
CFLAGS += -I../../libtikker/include -I../../third_party
|
||||
|
||||
BIN_DIR ?= ../../../build/bin
|
||||
LIB_DIR ?= ../../../build/lib
|
||||
LDFLAGS ?= -L$(LIB_DIR) -ltikker -lsqlite3 -lm
|
||||
|
||||
TARGET := $(BIN_DIR)/tikker-aggregator
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(BIN_DIR):
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
$(TARGET): main.c | $(BIN_DIR)
|
||||
@echo "Building tikker-aggregator..."
|
||||
@$(CC) $(CFLAGS) main.c -o $@ $(LDFLAGS)
|
||||
@echo "✓ tikker-aggregator built"
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGET)
|
||||
@echo "✓ aggregator cleaned"
|
||||
@@ -1,152 +0,0 @@
|
||||
#include <tikker.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options]\n\n", prog);
|
||||
printf("Options:\n");
|
||||
printf(" --daily Generate daily statistics\n");
|
||||
printf(" --hourly <date> Generate hourly stats for specific date\n");
|
||||
printf(" --weekly Generate weekly statistics\n");
|
||||
printf(" --weekday Generate weekday comparison\n");
|
||||
printf(" --top-keys [N] Show top N keys (default: 10)\n");
|
||||
printf(" --top-words [N] Show top N words (default: 10)\n");
|
||||
printf(" --format <format> Output format: json, csv, text (default: text)\n");
|
||||
printf(" --output <file> Write to file instead of stdout\n");
|
||||
printf(" --database <path> Use custom database (default: tikker.db)\n");
|
||||
printf(" --help Show this help message\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *db_path = "tikker.db";
|
||||
const char *action = NULL;
|
||||
const char *date_filter = NULL;
|
||||
const char *format = "text";
|
||||
const char *output_file = NULL;
|
||||
int top_count = 10;
|
||||
FILE *out = stdout;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "--database") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
db_path = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--daily") == 0) {
|
||||
action = "daily";
|
||||
} else if (strcmp(argv[i], "--hourly") == 0) {
|
||||
action = "hourly";
|
||||
if (i + 1 < argc) {
|
||||
date_filter = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--weekly") == 0) {
|
||||
action = "weekly";
|
||||
} else if (strcmp(argv[i], "--weekday") == 0) {
|
||||
action = "weekday";
|
||||
} else if (strcmp(argv[i], "--format") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
format = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--output") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
output_file = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--top-keys") == 0 || strcmp(argv[i], "--top-words") == 0) {
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
top_count = atoi(argv[++i]);
|
||||
if (top_count <= 0) top_count = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
fprintf(stderr, "Error: Please specify an action (--daily, --hourly, --weekly, or --weekday)\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (output_file) {
|
||||
out = fopen(output_file, "w");
|
||||
if (!out) {
|
||||
fprintf(stderr, "Error: Cannot open output file '%s'\n", output_file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tikker_context_t *ctx = tikker_open(db_path);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Error: Cannot open database '%s'\n", db_path);
|
||||
if (out != stdout) fclose(out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(action, "daily") == 0) {
|
||||
fprintf(out, "Daily Statistics\n");
|
||||
fprintf(out, "================\n\n");
|
||||
|
||||
uint64_t pressed, released, repeated;
|
||||
tikker_get_event_counts(ctx, &pressed, &released, &repeated);
|
||||
|
||||
fprintf(out, "Total Key Presses: %lu\n", (unsigned long)pressed);
|
||||
fprintf(out, "Total Releases: %lu\n", (unsigned long)released);
|
||||
fprintf(out, "Total Repeats: %lu\n", (unsigned long)repeated);
|
||||
fprintf(out, "Total Events: %lu\n", (unsigned long)(pressed + released + repeated));
|
||||
|
||||
} else if (strcmp(action, "hourly") == 0) {
|
||||
if (!date_filter) {
|
||||
fprintf(stderr, "Error: --hourly requires a date argument (YYYY-MM-DD)\n");
|
||||
tikker_close(ctx);
|
||||
if (out != stdout) fclose(out);
|
||||
return 1;
|
||||
}
|
||||
fprintf(out, "Hourly Statistics for %s\n", date_filter);
|
||||
fprintf(out, "========================\n\n");
|
||||
fprintf(out, "Hour Presses\n");
|
||||
fprintf(out, "----- -------\n");
|
||||
for (int h = 0; h < 24; h++) {
|
||||
fprintf(out, "%02d:00 ~1000\n", h);
|
||||
}
|
||||
|
||||
} else if (strcmp(action, "weekly") == 0) {
|
||||
fprintf(out, "Weekly Statistics\n");
|
||||
fprintf(out, "=================\n\n");
|
||||
fprintf(out, "Mon 12500 presses\n");
|
||||
fprintf(out, "Tue 13200 presses\n");
|
||||
fprintf(out, "Wed 12800 presses\n");
|
||||
fprintf(out, "Thu 11900 presses\n");
|
||||
fprintf(out, "Fri 13100 presses\n");
|
||||
fprintf(out, "Sat 8200 presses\n");
|
||||
fprintf(out, "Sun 9100 presses\n");
|
||||
|
||||
} else if (strcmp(action, "weekday") == 0) {
|
||||
fprintf(out, "Weekday Comparison\n");
|
||||
fprintf(out, "==================\n\n");
|
||||
fprintf(out, "Day Total Presses Avg Per Hour\n");
|
||||
fprintf(out, "--- -------- ----- --- ---- ----\n");
|
||||
fprintf(out, "Monday 12500 521\n");
|
||||
fprintf(out, "Tuesday 13200 550\n");
|
||||
fprintf(out, "Wednesday 12800 533\n");
|
||||
fprintf(out, "Thursday 11900 496\n");
|
||||
fprintf(out, "Friday 13100 546\n");
|
||||
fprintf(out, "Saturday 8200 342\n");
|
||||
fprintf(out, "Sunday 9100 379\n");
|
||||
}
|
||||
|
||||
tikker_close(ctx);
|
||||
if (out != stdout) fclose(out);
|
||||
|
||||
if (output_file) {
|
||||
printf("✓ Statistics written to %s\n", output_file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -pedantic -std=c11 -O2
|
||||
CFLAGS += -I../../libtikker/include -I../../third_party
|
||||
|
||||
BIN_DIR ?= ../../../build/bin
|
||||
LIB_DIR ?= ../../../build/lib
|
||||
LDFLAGS ?= -L$(LIB_DIR) -ltikker -lsqlite3 -lm
|
||||
|
||||
TARGET := $(BIN_DIR)/tikker-decoder
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(BIN_DIR):
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
$(TARGET): main.c | $(BIN_DIR)
|
||||
@echo "Building tikker-decoder..."
|
||||
@$(CC) $(CFLAGS) main.c -o $@ $(LDFLAGS)
|
||||
@echo "✓ tikker-decoder built"
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGET)
|
||||
@echo "✓ decoder cleaned"
|
||||
@@ -1,63 +0,0 @@
|
||||
#include <tikker.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options] <input_file> <output_file>\n", prog);
|
||||
printf("\nOptions:\n");
|
||||
printf(" --verbose Show processing progress\n");
|
||||
printf(" --stats Print decoding statistics\n");
|
||||
printf(" --help Show this help message\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int verbose = 0;
|
||||
int show_stats = 0;
|
||||
const char *input_file = NULL;
|
||||
const char *output_file = NULL;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--verbose") == 0) {
|
||||
verbose = 1;
|
||||
} else if (strcmp(argv[i], "--stats") == 0) {
|
||||
show_stats = 1;
|
||||
} else if (strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (argv[i][0] != '-') {
|
||||
if (!input_file) {
|
||||
input_file = argv[i];
|
||||
} else if (!output_file) {
|
||||
output_file = argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_file || !output_file) {
|
||||
fprintf(stderr, "Error: input and output files required\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("Decoding keylog: %s -> %s\n", input_file, output_file);
|
||||
}
|
||||
|
||||
int ret = tikker_decode_keylog(input_file, output_file);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error: Failed to decode keylog\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("✓ Decoding complete\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -pedantic -std=c11 -O2
|
||||
CFLAGS += -I../../libtikker/include -I../../third_party
|
||||
|
||||
BIN_DIR ?= ../../../build/bin
|
||||
LIB_DIR ?= ../../../build/lib
|
||||
LDFLAGS ?= -L$(LIB_DIR) -ltikker -lsqlite3 -lm
|
||||
|
||||
TARGET := $(BIN_DIR)/tikker-indexer
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(BIN_DIR):
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
$(TARGET): main.c | $(BIN_DIR)
|
||||
@echo "Building tikker-indexer..."
|
||||
@$(CC) $(CFLAGS) main.c -o $@ $(LDFLAGS)
|
||||
@echo "✓ tikker-indexer built"
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGET)
|
||||
@echo "✓ indexer cleaned"
|
||||
@@ -1,135 +0,0 @@
|
||||
#include <tikker.h>
|
||||
#include <indexer.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options]\n\n", prog);
|
||||
printf("Options:\n");
|
||||
printf(" --index Build word index from logs_plain directory\n");
|
||||
printf(" --popular [N] Show top N most popular words (default: 10)\n");
|
||||
printf(" --find <word> Find frequency of a specific word\n");
|
||||
printf(" --database <path> Use custom database (default: tags.db)\n");
|
||||
printf(" --help Show this help message\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *db_path = "tags.db";
|
||||
const char *action = NULL;
|
||||
const char *word_to_find = NULL;
|
||||
int popular_count = 10;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "--database") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
db_path = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--index") == 0) {
|
||||
action = "index";
|
||||
} else if (strcmp(argv[i], "--popular") == 0) {
|
||||
action = "popular";
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
popular_count = atoi(argv[++i]);
|
||||
if (popular_count <= 0) popular_count = 10;
|
||||
}
|
||||
} else if (strcmp(argv[i], "--find") == 0) {
|
||||
action = "find";
|
||||
if (i + 1 < argc) {
|
||||
word_to_find = argv[++i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
fprintf(stderr, "Error: Please specify an action (--index, --popular, or --find)\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(action, "index") == 0) {
|
||||
printf("Building word index from logs_plain directory...\n");
|
||||
int ret = tikker_index_directory("logs_plain", db_path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error: Failed to index directory\n");
|
||||
return 1;
|
||||
}
|
||||
printf("✓ Index built successfully\n");
|
||||
|
||||
int unique_count;
|
||||
tikker_word_get_unique_count(db_path, &unique_count);
|
||||
printf(" Total unique words: %d\n", unique_count);
|
||||
|
||||
uint64_t total_count;
|
||||
tikker_word_get_total_count(db_path, &total_count);
|
||||
printf(" Total word count: %lu\n", (unsigned long)total_count);
|
||||
|
||||
} else if (strcmp(action, "popular") == 0) {
|
||||
printf("Top %d most popular words:\n\n", popular_count);
|
||||
printf("%-5s %-20s %10s %10s\n", "#", "Word", "Count", "Percent");
|
||||
printf("%-5s %-20s %10s %10s\n", "-", "----", "-----", "-------");
|
||||
|
||||
uint64_t total_count;
|
||||
tikker_word_get_total_count(db_path, &total_count);
|
||||
|
||||
if (total_count == 0) {
|
||||
printf("No words indexed yet. Run with --index first.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tikker_word_entry_t *entries;
|
||||
int count;
|
||||
int ret = tikker_word_get_top(db_path, popular_count, &entries, &count);
|
||||
if (ret != 0 || count == 0) {
|
||||
printf("No words found in database.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int j = 0; j < count; j++) {
|
||||
double percent = (double)entries[j].count / total_count * 100.0;
|
||||
printf("#%-4d %-20s %10lu %9.2f%%\n",
|
||||
entries[j].rank,
|
||||
entries[j].word,
|
||||
(unsigned long)entries[j].count,
|
||||
percent);
|
||||
}
|
||||
|
||||
tikker_word_entries_free(entries, count);
|
||||
|
||||
} else if (strcmp(action, "find") == 0) {
|
||||
if (!word_to_find) {
|
||||
fprintf(stderr, "Error: --find requires a word argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t count;
|
||||
int rank;
|
||||
int ret = tikker_word_get_rank(db_path, word_to_find, &rank, &count);
|
||||
|
||||
if (ret != 0) {
|
||||
uint64_t freq;
|
||||
tikker_word_get_frequency(db_path, word_to_find, &freq);
|
||||
if (freq > 0) {
|
||||
printf("Word: '%s'\n", word_to_find);
|
||||
printf("Frequency: %lu\n", (unsigned long)freq);
|
||||
} else {
|
||||
printf("Word '%s' not found in database.\n", word_to_find);
|
||||
}
|
||||
} else {
|
||||
printf("Word: '%s'\n", word_to_find);
|
||||
printf("Rank: #%d\n", rank);
|
||||
printf("Frequency: %lu\n", (unsigned long)count);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -pedantic -std=c11 -O2
|
||||
CFLAGS += -I../../libtikker/include -I../../third_party
|
||||
|
||||
BIN_DIR ?= ../../../build/bin
|
||||
LIB_DIR ?= ../../../build/lib
|
||||
LDFLAGS ?= -L$(LIB_DIR) -ltikker -lsqlite3 -lm
|
||||
|
||||
TARGET := $(BIN_DIR)/tikker-report
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(BIN_DIR):
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
$(TARGET): main.c | $(BIN_DIR)
|
||||
@echo "Building tikker-report..."
|
||||
@$(CC) $(CFLAGS) main.c -o $@ $(LDFLAGS)
|
||||
@echo "✓ tikker-report built"
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGET)
|
||||
@echo "✓ report cleaned"
|
||||
@@ -1,123 +0,0 @@
|
||||
#include <tikker.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options]\n\n", prog);
|
||||
printf("Options:\n");
|
||||
printf(" --input <dir> Input logs directory (default: logs_plain)\n");
|
||||
printf(" --output <file> Output HTML file (default: report.html)\n");
|
||||
printf(" --graph-dir <dir> Directory with PNG graphs to embed\n");
|
||||
printf(" --include-graphs Include embedded PNG graphs (requires --graph-dir)\n");
|
||||
printf(" --database <path> Use custom database (default: tikker.db)\n");
|
||||
printf(" --title <title> Report title\n");
|
||||
printf(" --help Show this help message\n");
|
||||
}
|
||||
|
||||
int count_graph_files(const char *dir) {
|
||||
if (!dir) return 0;
|
||||
|
||||
DIR *d = opendir(dir);
|
||||
if (!d) return 0;
|
||||
|
||||
struct dirent *entry;
|
||||
int count = 0;
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
if (strstr(entry->d_name, ".png")) count++;
|
||||
}
|
||||
closedir(d);
|
||||
return count;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *input_dir = "logs_plain";
|
||||
const char *output_file = "report.html";
|
||||
const char *graph_dir = NULL;
|
||||
const char *db_path = "tikker.db";
|
||||
const char *title = "Tikker Activity Report";
|
||||
int include_graphs = 0;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
} else if (strcmp(argv[i], "--input") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
input_dir = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--output") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
output_file = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--graph-dir") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
graph_dir = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--include-graphs") == 0) {
|
||||
include_graphs = 1;
|
||||
} else if (strcmp(argv[i], "--database") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
db_path = argv[++i];
|
||||
}
|
||||
} else if (strcmp(argv[i], "--title") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
title = argv[++i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Generating report...\n");
|
||||
printf(" Input directory: %s\n", input_dir);
|
||||
printf(" Output file: %s\n", output_file);
|
||||
|
||||
tikker_context_t *ctx = tikker_open(db_path);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Error: Cannot open database '%s'\n", db_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (tikker_generate_html_report(ctx, output_file, graph_dir) != 0) {
|
||||
fprintf(stderr, "Error: Failed to generate report\n");
|
||||
tikker_close(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *out = fopen(output_file, "a");
|
||||
if (out) {
|
||||
fprintf(out, "\n<!-- Report Statistics -->\n");
|
||||
fprintf(out, "<div class='stats'>\n");
|
||||
fprintf(out, "<h2>Statistics</h2>\n");
|
||||
fprintf(out, "<p>Report generated at: %s</p>\n", __DATE__);
|
||||
|
||||
uint64_t pressed, released, repeated;
|
||||
tikker_get_event_counts(ctx, &pressed, &released, &repeated);
|
||||
|
||||
fprintf(out, "<p>Total Key Presses: %lu</p>\n", (unsigned long)pressed);
|
||||
fprintf(out, "<p>Total Releases: %lu</p>\n", (unsigned long)released);
|
||||
fprintf(out, "<p>Total Repeats: %lu</p>\n", (unsigned long)repeated);
|
||||
|
||||
if (include_graphs && graph_dir) {
|
||||
int graph_count = count_graph_files(graph_dir);
|
||||
fprintf(out, "<p>Graphs embedded: %d</p>\n", graph_count);
|
||||
}
|
||||
|
||||
fprintf(out, "</div>\n");
|
||||
fprintf(out, "</body>\n");
|
||||
fprintf(out, "</html>\n");
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
tikker_close(ctx);
|
||||
|
||||
printf("✓ Report generated: %s\n", output_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user