dodo/src/dodo.c
2024-09-07 19:27:27 -07:00

289 lines
6.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"
#include "x_string.h"
#include "x_curses.h"
#define SQLQUERY_MAX 100
#define ARG_MAX 100
#define TODAY_COL_START -1
#define BACKLOG_COL_START 33
#define BLOCKED_COL_START 66
#define FIXED_WIDTH 19
int checksqlerr(int rc, char *errmsg){
if( rc!=SQLITE_OK ){
fprintf(stderr, "rc = %d\n", rc);
fprintf(stderr, "SQL error: %s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
return 0;
}
int opendb(sqlite3 **db, char* filename){
int rc = 0;
rc = sqlite3_open(filename, db);
if ( rc != 0 ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(*db));
sqlite3_close(*db);
return(rc);
}
return(rc);
}
// FOR DEBUG
int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
// FOR DEBUG
int view_all(sqlite3 *db){
int rc = 0;
char* errmsg;
//rc = sqlite3_exec(db, "SELECT * FROM all_info;", print_select, 0, &errmsg);
rc = sqlite3_exec(db, "SELECT * FROM tasks;", callback, 0, &errmsg);
//rc = sqlite3_exec(db, "SELECT * FROM entries;", print_select, 0, &errmsg);
checksqlerr(rc, errmsg);
return rc;
}
// Print Heading TODO: obvi make this better
void display_heading(){
printf("\n");
printf("%s Today %s", X_BOLD, X_RST);
printf(" %s Backlog %s", X_BOLD, X_RST);
printf(" %s Blocked %s", X_BOLD, X_RST);
printf("\n");
printf("%s%stitle due date %s", X_BOLD, X_UNDL, X_RST);
printf(" %s%stitle due date %s", X_BOLD, X_UNDL, X_RST);
printf(" %s%stitle due date %s", X_BOLD, X_UNDL, X_RST);
printf("\n");
}
int gen_sql_insert_stmt(sqlite3 *db, sqlite3_stmt** out_stmt, char* table, char* colnames, char* values){
char sql_query[SQLQUERY_MAX];
int rc = 0;
snprintf(sql_query, SQLQUERY_MAX, "INSERT INTO %s %s VALUES %s", table, colnames, values);
rc = sqlite3_prepare_v2(db, sql_query, -1, out_stmt, NULL);
if (checksqlerr(rc, "prepare broken in gen_sql_insert_stmt")){
return -1;
}
}
// Generate the sql statement by giving the columns, table, and current task list wanted
int gen_sql_select_stmt(sqlite3 *db, sqlite3_stmt** out_stmt, char* colnames, char* table, char* status){
char sql_query[SQLQUERY_MAX];
int rc = 0;
snprintf(sql_query, SQLQUERY_MAX, "SELECT %s FROM %s WHERE status='%s'", colnames, table, status);
rc = sqlite3_prepare_v2(db, sql_query, -1, out_stmt, NULL);
if (checksqlerr(rc, "prepare broken in gen_sql_select_stmt")){
return -1;
}
return 0;
}
// Get number of tasks from tasks table give status
int get_num_rows(sqlite3 *db, char* table, char* status){
sqlite3_stmt *out_stmt;
int rc = 0;
char *col_name = "COUNT(*)";
if ( gen_sql_select_stmt(db, &out_stmt, col_name, table, status) == 1 ){
return -1;
}
while ( rc = sqlite3_step(out_stmt) == SQLITE_ROW ){
return sqlite3_column_int(out_stmt, 0);
}
return -1;
}
// Print with FIXED_WIDTH
int print_fixed_width(const unsigned char* str){
if (str){
printf("%-*.*s", FIXED_WIDTH, FIXED_WIDTH, str);
}else{
printf("%-*.*s", FIXED_WIDTH, FIXED_WIDTH, "");
}
}
int display_task_list(int start_col, sqlite3 *db, char* colnames, char* table, char* status){
static int max_rows = -1;
int rc = 0;
int i = 0;
int num_rows = -1;
int num_cols = 0;
char* errmsg;
const unsigned char* col_val;
sqlite3_stmt* out_stmt;
// Start col for the current task list
X_goright(start_col);
// Get the number of rows in current task list
num_rows = get_num_rows(db, table, status);
// Then keep track of the furthest down we go
if (num_rows > max_rows){
max_rows = num_rows;
}
// Generate the sql statement by giving the columns, table, and current task list wanted
if ( gen_sql_select_stmt(db, &out_stmt, colnames, table, status) ){
return -1;
}
// TODO: prob should be a func begin
// while there is still rows available
while ( rc = sqlite3_step(out_stmt) == SQLITE_ROW ){
// for each column print the column
num_cols = sqlite3_column_count(out_stmt);
for (i = 0; i < num_cols; i++){
col_val = sqlite3_column_text(out_stmt, i);
print_fixed_width(col_val);
}
// move down one and over to the start of the current task column
printf("\n");
// end
X_goright(start_col);
}
// if while loop broke and rc returned an error
if ( rc == SQLITE_ERROR ){
X_godown(max_rows);
checksqlerr(rc, "step broken in display_task_list");
return -1;
}
// Once the task list is completely printed
// reset to the top of the task lists
if (num_rows > 0){
X_goup(num_rows);
}
// if it is the last task list move down one past the longest list
if ( status == "blocked"){
X_godown(max_rows);
}
return 0;
}
// pass in the args and return the title and due date
// due date passed as NULL if for delete
// TODO input validation for strings implement in strings!
int parse_args(int argc, char** argv, char** title, char** due_date){
if ( argc > 1 ){
*title = argv[2];
}
if ( argc > 2 ){
*due_date = argv[3];
}
}
// TODO: the way this ensures that we are only passing in
// valid inputs is stupid and ugly FIX
int add_task(sqlite3 *db, int argc, char** argv){
int rc = 0;
char* table = "tasks";
char* title;
char* due_date;
char* colnames = "(title, due_date)";
char values[100];
sqlite3_stmt* out_stmt;
parse_args(argc, argv, &title, &due_date);
if ( due_date != NULL ){
snprintf(values, 100, "('%s', '%s')", title, due_date);
}else{
colnames = "(title)";
snprintf(values, 100, "('%s')", title);
}
if ( gen_sql_insert_stmt(db, &out_stmt, table, colnames, values) ){
return -1;
}
if ( ( rc = sqlite3_step(out_stmt) ) == SQLITE_DONE){
return 0;
}
checksqlerr(rc, "broken in add_task");
return 1;
}
// Print kanban table
// All lists with task name and due date
int view(sqlite3 *db){
char* table = "tasks";
char* status = "today";
char* colnames = "title,due_date";
// Print Heading
display_heading();
// Print "today" tasks
if ( display_task_list(TODAY_COL_START, db, colnames, table, status) ){
return -1;
}
// Print "backlog" tasks
status = "backlog";
if ( display_task_list(BACKLOG_COL_START, db, colnames, table, status) ){
return -1;
}
// Print "blocked" tasks
status = "blocked";
if ( display_task_list(BLOCKED_COL_START, db, colnames, table, status) ){
return -1;
}
return 0;
}
int main( int argc, char **argv ){
sqlite3 *db;
int rc = 0;
char* home_dir = getenv("HOME");
char* filename = x_strconcat(home_dir, DB_PATH);
rc = opendb(&db, filename);
if ( argv[1] ){
if (argv[1][0] == 'b'){
rc = view(db);
}
else if ( argv[1][0] == 'a'){
rc = add_task(db,argc,argv);
}
else if ( argv[1][0] == 'v'){
rc = view_all(db);
}
}
else{
rc = view(db);
}
sqlite3_close(db);
return 0;
}