Compare commits
24 commits
Author | SHA1 | Date | |
---|---|---|---|
59f56c1a6e | |||
0509c37e38 | |||
d05b7735b2 | |||
5a8e3a073a | |||
95e0ebc073 | |||
f220724d42 | |||
d6cb8f0ce8 | |||
c13b361de2 | |||
7fe5c0e8a8 | |||
89b7f484f7 | |||
45d537d264 | |||
307c671a65 | |||
445e883f55 | |||
cbbbf68db0 | |||
9905e9fada | |||
ed063a7483 | |||
4ed84f160d | |||
2c967d7079 | |||
de12720d2a | |||
be20e4fab1 | |||
e0610cee1c | |||
bcb08712c6 | |||
b5773c0a03 | |||
9399f71406 |
20 changed files with 1893 additions and 1 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
cmake-*-debug
|
||||||
|
*.db
|
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
cmake_minimum_required(VERSION 3.31)
|
||||||
|
project(blist C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 90)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
|
@ -1,3 +1,3 @@
|
||||||
# klist
|
# blist
|
||||||
|
|
||||||
todos for your browser and terminal
|
todos for your browser and terminal
|
2
src/CMakeLists.txt
Normal file
2
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
add_subdirectory(cli)
|
||||||
|
add_subdirectory(libblist)
|
4
src/cli/CMakeLists.txt
Normal file
4
src/cli/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
add_executable(blist main.c process.c config.c util.c)
|
||||||
|
target_include_directories(blist PRIVATE include)
|
||||||
|
target_link_libraries(blist libblist)
|
||||||
|
target_compile_options(blist PRIVATE -Werror -Wall) #-Wextra)
|
168
src/cli/config.c
Normal file
168
src/cli/config.c
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
char *blist_config_key_to_string(BLIST_CONFIG_KEY key) {
|
||||||
|
switch (key) {
|
||||||
|
case CONFIG_DATABASE_NAME:
|
||||||
|
return "database";
|
||||||
|
case _CONFIG_KEY_COUNT:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
BLIST_CONFIG_KEY blist_config_string_to_key(const char *key) {
|
||||||
|
if (!strcmp(key, blist_config_key_to_string(CONFIG_DATABASE_NAME)))
|
||||||
|
return CONFIG_DATABASE_NAME;
|
||||||
|
return _CONFIG_KEY_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *blist_config_name(char *name) {
|
||||||
|
char *config_home = getenv("XDG_CONFIG_HOME");
|
||||||
|
bool config_home_a = false;
|
||||||
|
if (!config_home) {
|
||||||
|
config_home_a = true;
|
||||||
|
char *home = getenv("HOME");
|
||||||
|
const char *config_home_format = "%s/.config";
|
||||||
|
int config_home_len = snprintf(name, 0, config_home_format, home);
|
||||||
|
config_home = malloc(++config_home_len * sizeof(char));
|
||||||
|
snprintf(config_home, config_home_len, config_home_format, home, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_dir_len = snprintf(NULL, 0, "%s/%s", config_home, name);
|
||||||
|
char *config_dir = malloc(++config_dir_len * sizeof(char));
|
||||||
|
snprintf(config_dir, config_dir_len, "%s/%s", config_home, name);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(config_dir, &st) == -1)
|
||||||
|
mkdir(config_dir, 0755);
|
||||||
|
|
||||||
|
int config_file_len = snprintf(NULL, 0, "%s/%s/config", config_home, name);
|
||||||
|
char *config_file_name = malloc(++config_file_len * sizeof(char));
|
||||||
|
snprintf(config_file_name, config_file_len, "%s/%s/config", config_home,
|
||||||
|
name);
|
||||||
|
|
||||||
|
if (config_home_a)
|
||||||
|
free(config_home);
|
||||||
|
free(config_dir);
|
||||||
|
|
||||||
|
return config_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_config_parse(blist_config *ctx, FILE *config) {
|
||||||
|
fseek(config, 0, SEEK_SET);
|
||||||
|
int c;
|
||||||
|
char *key = NULL, *val = NULL;
|
||||||
|
int key_len = 0, val_len = 0;
|
||||||
|
bool found_key = false, ignore_line = false;
|
||||||
|
while ((c = fgetc(config)) != EOF) {
|
||||||
|
switch (c) {
|
||||||
|
case '\n':
|
||||||
|
ignore_line = false;
|
||||||
|
if (!key)
|
||||||
|
continue;
|
||||||
|
if (!val) {
|
||||||
|
fprintf(stderr, "ignoring key '%s'", key);
|
||||||
|
}
|
||||||
|
if (!strcmp(key, blist_config_key_to_string(CONFIG_DATABASE_NAME))) {
|
||||||
|
*ctx[CONFIG_DATABASE_NAME] = malloc((strlen(val) + 1) * sizeof(char));
|
||||||
|
strcpy(*ctx[CONFIG_DATABASE_NAME], val);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[init] ignoring unknown key '%s'", key);
|
||||||
|
}
|
||||||
|
free(key);
|
||||||
|
free(val);
|
||||||
|
key_len = 0, val_len = 0;
|
||||||
|
key = NULL, val = NULL;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (ignore_line)
|
||||||
|
continue;
|
||||||
|
if (!found_key)
|
||||||
|
found_key = true;
|
||||||
|
else {
|
||||||
|
printf("config file is screwed, please review\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
if (!key && !val)
|
||||||
|
ignore_line = true;
|
||||||
|
default:
|
||||||
|
if (ignore_line)
|
||||||
|
continue;
|
||||||
|
if (found_key) {
|
||||||
|
if (val) {
|
||||||
|
char *_val = realloc(val, ++val_len * sizeof(char));
|
||||||
|
if (_val)
|
||||||
|
val = _val;
|
||||||
|
} else {
|
||||||
|
val_len = 2;
|
||||||
|
val = malloc(val_len * sizeof(char));
|
||||||
|
}
|
||||||
|
val[val_len - 2] = (char)c;
|
||||||
|
val[val_len - 1] = '\0';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (key) {
|
||||||
|
char *_key = realloc(key, ++key_len * sizeof(char));
|
||||||
|
if (_key)
|
||||||
|
key = _key;
|
||||||
|
} else {
|
||||||
|
key_len = 2;
|
||||||
|
key = malloc(key_len * sizeof(char));
|
||||||
|
}
|
||||||
|
key[key_len - 2] = (char)c;
|
||||||
|
key[key_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key)
|
||||||
|
free(key);
|
||||||
|
if (val)
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_config_setup(char *name, FILE *config) {
|
||||||
|
char *data_home = getenv("XDG_DATA_HOME");
|
||||||
|
bool data_home_a = false;
|
||||||
|
if (!data_home) {
|
||||||
|
data_home_a = true;
|
||||||
|
char *home = getenv("HOME");
|
||||||
|
const char *data_home_format = "%s/.local/state";
|
||||||
|
int data_home_len = snprintf(name, 0, data_home_format, home);
|
||||||
|
data_home = malloc(++data_home_len * sizeof(char));
|
||||||
|
snprintf(data_home, data_home_len, data_home_format, home, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int db_file_len = snprintf(NULL, 0, "%s/%s", data_home, name);
|
||||||
|
char *db_file = malloc(++db_file_len * sizeof(char));
|
||||||
|
snprintf(db_file, db_file_len, "%s/%s", data_home, name);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(db_file, &st) == -1)
|
||||||
|
mkdir(db_file, 0755);
|
||||||
|
|
||||||
|
if (data_home_a)
|
||||||
|
free(data_home);
|
||||||
|
|
||||||
|
// fseek(config, 0, SEEK_END);
|
||||||
|
fprintf(config, "# -- blist config file --\n"
|
||||||
|
"# please note that keys and values are case and space "
|
||||||
|
"sensitive, only key=value is parsable\n"
|
||||||
|
"# valid keys: ");
|
||||||
|
int i = 0;
|
||||||
|
for (; i < _CONFIG_KEY_COUNT; i++)
|
||||||
|
fprintf(config, "%s,", blist_config_key_to_string(i));
|
||||||
|
fprintf(config, "\n\n");
|
||||||
|
fprintf(config, "%s=%s/tasks.db\n",
|
||||||
|
blist_config_key_to_string(CONFIG_DATABASE_NAME), db_file);
|
||||||
|
fclose(config);
|
||||||
|
|
||||||
|
if (db_file)
|
||||||
|
free(db_file);
|
||||||
|
}
|
14
src/cli/include/config.h
Normal file
14
src/cli/include/config.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
enum BLIST_CONFIG_KEY { CONFIG_DATABASE_NAME, _CONFIG_KEY_COUNT };
|
||||||
|
typedef enum BLIST_CONFIG_KEY BLIST_CONFIG_KEY;
|
||||||
|
|
||||||
|
typedef char *blist_config[_CONFIG_KEY_COUNT];
|
||||||
|
|
||||||
|
char *blist_config_key_to_string(BLIST_CONFIG_KEY key);
|
||||||
|
BLIST_CONFIG_KEY blist_config_string_to_key(const char *key);
|
||||||
|
|
||||||
|
char *blist_config_name(char *);
|
||||||
|
void blist_config_parse(blist_config *, FILE *);
|
||||||
|
void blist_config_setup(char *, FILE *);
|
17
src/cli/include/process.h
Normal file
17
src/cli/include/process.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
int blist_app_user_get(const blist_app *);
|
||||||
|
int blist_app_user_create(const blist_app *);
|
||||||
|
int blist_app_user_delete(const blist_app *);
|
||||||
|
|
||||||
|
int blist_app_list_add(const blist_app *);
|
||||||
|
int blist_app_list_edit(const blist_app *);
|
||||||
|
int blist_app_list_get(const blist_app *);
|
||||||
|
int blist_app_list_delete(const blist_app *);
|
||||||
|
|
||||||
|
int blist_app_task_add(const blist_app *);
|
||||||
|
int blist_app_task_edit(const blist_app *);
|
||||||
|
int blist_app_task_get(const blist_app *);
|
||||||
|
int blist_app_task_delete(const blist_app *);
|
73
src/cli/include/util.h
Normal file
73
src/cli/include/util.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
#include "util.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "blist.h"
|
||||||
|
|
||||||
|
enum blist_command {
|
||||||
|
USER,
|
||||||
|
LIST,
|
||||||
|
TASK,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct blist_app {
|
||||||
|
enum blist_command cmd;
|
||||||
|
void *cmd_ctx;
|
||||||
|
int error;
|
||||||
|
blist_logging_ctx *log_ctx;
|
||||||
|
blist *handle;
|
||||||
|
char *config;
|
||||||
|
};
|
||||||
|
typedef struct blist_app blist_app;
|
||||||
|
|
||||||
|
blist_app *blist_app_init(char *db);
|
||||||
|
void blist_app_deinit(blist_app *app);
|
||||||
|
|
||||||
|
enum blist_user_command { USER_GET, USER_CREATE, USER_DELETE };
|
||||||
|
struct blist_user_context {
|
||||||
|
enum blist_user_command cmd;
|
||||||
|
};
|
||||||
|
typedef struct blist_user_context blist_user_context;
|
||||||
|
|
||||||
|
blist_user_context *blist_user_context_init(void);
|
||||||
|
void blist_user_context_deinit(blist_user_context *ctx);
|
||||||
|
|
||||||
|
enum blist_list_command {
|
||||||
|
LIST_ADD,
|
||||||
|
LIST_EDIT,
|
||||||
|
LIST_GET,
|
||||||
|
LIST_DELETE,
|
||||||
|
};
|
||||||
|
struct blist_list_context {
|
||||||
|
enum blist_list_command cmd;
|
||||||
|
char *name;
|
||||||
|
char *desc;
|
||||||
|
char **stages;
|
||||||
|
size_t stages_len;
|
||||||
|
char *preset;
|
||||||
|
};
|
||||||
|
typedef struct blist_list_context blist_list_context;
|
||||||
|
|
||||||
|
blist_list_context *blist_list_context_init(void);
|
||||||
|
blist_list_context *blist_list_context_get_by_id(blist_list_context *ctx,
|
||||||
|
u_int id);
|
||||||
|
void blist_list_context_deinit(blist_list_context *ctx);
|
||||||
|
|
||||||
|
enum blist_task_command {
|
||||||
|
TASK_ADD,
|
||||||
|
TASK_EDIT,
|
||||||
|
TASK_GET,
|
||||||
|
TASK_DELETE,
|
||||||
|
};
|
||||||
|
struct blist_task_context {
|
||||||
|
enum blist_task_command cmd;
|
||||||
|
char *list;
|
||||||
|
char *name;
|
||||||
|
char *desc;
|
||||||
|
char *stage;
|
||||||
|
};
|
||||||
|
typedef struct blist_task_context blist_task_context;
|
||||||
|
|
||||||
|
blist_task_context *blist_task_context_init(void);
|
||||||
|
void blist_task_context_deinit(blist_task_context *ctx);
|
252
src/cli/main.c
Normal file
252
src/cli/main.c
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "process.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void print_help(char **argv);
|
||||||
|
char *prepare(int argc, char **argv);
|
||||||
|
void setup(blist_app *ctx, int argc, char **argv);
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
char *name = "blist";
|
||||||
|
blist_config config;
|
||||||
|
|
||||||
|
char *config_file_name = prepare(argc, argv);
|
||||||
|
if (!config_file_name)
|
||||||
|
config_file_name = blist_config_name(name);
|
||||||
|
|
||||||
|
FILE *config_file = NULL;
|
||||||
|
if (access(config_file_name, F_OK) == -1) {
|
||||||
|
config_file = fopen(config_file_name, "w");
|
||||||
|
blist_config_setup(name, config_file);
|
||||||
|
} else
|
||||||
|
config_file = fopen(config_file_name, "a+");
|
||||||
|
|
||||||
|
blist_config_parse(&config, config_file);
|
||||||
|
|
||||||
|
blist_app *ctx = blist_app_init(config[CONFIG_DATABASE_NAME]);
|
||||||
|
|
||||||
|
free(config_file_name);
|
||||||
|
free(config[CONFIG_DATABASE_NAME]);
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
print_help(argv);
|
||||||
|
ctx->error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->error) {
|
||||||
|
setup(ctx, argc, argv);
|
||||||
|
switch (ctx->cmd) {
|
||||||
|
case USER:
|
||||||
|
blist_user_context *user_ctx = ctx->cmd_ctx;
|
||||||
|
switch (user_ctx->cmd) {
|
||||||
|
case USER_GET:
|
||||||
|
blist_app_user_get(ctx);
|
||||||
|
break;
|
||||||
|
case USER_CREATE:
|
||||||
|
blist_app_user_create(ctx);
|
||||||
|
break;
|
||||||
|
case USER_DELETE:
|
||||||
|
blist_app_user_delete(ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
blist_user_context_deinit(user_ctx);
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
blist_list_context *list_ctx = ctx->cmd_ctx;
|
||||||
|
if (!list_ctx->name && list_ctx->cmd != LIST_GET) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Missing name.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (list_ctx->cmd) {
|
||||||
|
case LIST_ADD:
|
||||||
|
blist_app_list_add(ctx);
|
||||||
|
break;
|
||||||
|
case LIST_EDIT:
|
||||||
|
break;
|
||||||
|
case LIST_GET:
|
||||||
|
blist_app_list_get(ctx);
|
||||||
|
break;
|
||||||
|
case LIST_DELETE:
|
||||||
|
blist_app_list_delete(ctx);
|
||||||
|
}
|
||||||
|
if (list_ctx)
|
||||||
|
blist_list_context_deinit(list_ctx);
|
||||||
|
break;
|
||||||
|
case TASK:
|
||||||
|
blist_task_context *task_ctx = ctx->cmd_ctx;
|
||||||
|
switch (((blist_task_context *)ctx->cmd_ctx)->cmd) {
|
||||||
|
case TASK_ADD: // basically the same things happen and edit can create if
|
||||||
|
// necessary
|
||||||
|
case TASK_EDIT:
|
||||||
|
blist_app_task_edit(ctx);
|
||||||
|
break;
|
||||||
|
case TASK_GET:
|
||||||
|
blist_app_task_get(ctx);
|
||||||
|
break;
|
||||||
|
case TASK_DELETE:
|
||||||
|
blist_app_task_delete(ctx);
|
||||||
|
}
|
||||||
|
blist_task_context_deinit(task_ctx);
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int error = ctx->error;
|
||||||
|
blist_app_deinit(ctx);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_help(char **argv) { printf("Usage: %s <action>\n", argv[0]); }
|
||||||
|
|
||||||
|
char *prepare(int argc, char **argv) {
|
||||||
|
char *config = NULL;
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "hc")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'c':
|
||||||
|
config = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_help(argv);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(blist_app *ctx, int argc, char **argv) {
|
||||||
|
if (strcmp(argv[1], "user") == 0)
|
||||||
|
ctx->cmd = USER;
|
||||||
|
else if (strcmp(argv[1], "list") == 0)
|
||||||
|
ctx->cmd = LIST;
|
||||||
|
else if (strcmp(argv[1], "task") == 0)
|
||||||
|
ctx->cmd = TASK;
|
||||||
|
else
|
||||||
|
print_help(argv);
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
switch (ctx->cmd) {
|
||||||
|
case USER:
|
||||||
|
optind = 2;
|
||||||
|
blist_user_context *user_ctx = blist_user_context_init();
|
||||||
|
while ((opt = getopt(argc, argv, "cdg")) != -1)
|
||||||
|
switch (opt) {
|
||||||
|
case 'c':
|
||||||
|
user_ctx->cmd = USER_CREATE;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
user_ctx->cmd = USER_DELETE;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
user_ctx->cmd = USER_GET;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_help(argv);
|
||||||
|
}
|
||||||
|
ctx->cmd_ctx = user_ctx;
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
blist_list_context *list_ctx = blist_list_context_init();
|
||||||
|
ctx->cmd_ctx = list_ctx;
|
||||||
|
if (argc < 3) {
|
||||||
|
list_ctx->cmd = LIST_GET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
optind = 2;
|
||||||
|
while ((opt = getopt(argc, argv, "aden:p:s:")) != -1)
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
list_ctx->cmd = LIST_ADD;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
list_ctx->cmd = LIST_EDIT;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
list_ctx->cmd = LIST_DELETE;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
list_ctx->preset = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
list_ctx->name = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
char *stage = NULL;
|
||||||
|
while ((stage = strsep(&optarg, ","))) {
|
||||||
|
char **stages = realloc(list_ctx->stages,
|
||||||
|
(++list_ctx->stages_len) * sizeof(char *));
|
||||||
|
if (stages) {
|
||||||
|
list_ctx->stages = stages;
|
||||||
|
list_ctx->stages[list_ctx->stages_len - 1] = strdup(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_help(argv);
|
||||||
|
fprintf(stderr,
|
||||||
|
""
|
||||||
|
"list options:\n"
|
||||||
|
"-a\t\tadd a list\n"
|
||||||
|
"-e\t\tedit a list\n"
|
||||||
|
"-d\t\tdelete a list\n"
|
||||||
|
"-p <preset>\tdefine as preset or use existing one with name "
|
||||||
|
"<preset>\n"
|
||||||
|
"Not providing a parameter prints all lists and existing "
|
||||||
|
"presets.\n\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TASK:
|
||||||
|
blist_task_context *task_ctx = blist_task_context_init();
|
||||||
|
|
||||||
|
optind = 2;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "aedgl:n:s:")) != -1)
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
task_ctx->cmd = TASK_ADD;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
task_ctx->cmd = TASK_EDIT;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
task_ctx->cmd = TASK_DELETE;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
task_ctx->cmd = TASK_GET;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
task_ctx->list = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
task_ctx->name = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
task_ctx->stage = strdup(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_help(argv);
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
""
|
||||||
|
"task options:"
|
||||||
|
"-a\tadd a task"
|
||||||
|
"-e\tedit a task"
|
||||||
|
"-d\tdelete a task"
|
||||||
|
"Not providing a parameter prints all tasks of a list.");
|
||||||
|
}
|
||||||
|
if (!task_ctx->cmd)
|
||||||
|
task_ctx->cmd = TASK_ADD;
|
||||||
|
ctx->cmd_ctx = task_ctx;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"How did we land here?! Pls report argv[1] = %s\n", argv[1]);
|
||||||
|
print_help(argv);
|
||||||
|
}
|
||||||
|
}
|
295
src/cli/process.c
Normal file
295
src/cli/process.c
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "models.h"
|
||||||
|
|
||||||
|
int blist_app_user_get(const blist_app *ctx) {
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
printf("User: %s\nID: %lu\n", (char *)user->name, user->id);
|
||||||
|
size_t lists_len = 0;
|
||||||
|
blist_list **lists =
|
||||||
|
blist_list_get_all_by_user(ctx->handle, user->id, &lists_len);
|
||||||
|
printf("Lists: %lu\n", lists_len);
|
||||||
|
int i = 0;
|
||||||
|
for (; i < lists_len; i++)
|
||||||
|
blist_list_deinit(lists[i]);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "No user for '%s' found.\n",
|
||||||
|
getlogin());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_user_create(const blist_app *ctx) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_INFO,
|
||||||
|
(blist_assure_user(ctx->handle, getuid(), getlogin()))
|
||||||
|
? "User created.\n"
|
||||||
|
: "User already exists.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_user_delete(const blist_app *ctx) {
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
if (user) {
|
||||||
|
blist_user_delete(ctx->handle, user);
|
||||||
|
printf("User deleted.\n");
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"User not found, no changes done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_list_add(const blist_app *ctx) {
|
||||||
|
blist_list_context *list_ctx = ctx->cmd_ctx;
|
||||||
|
if (!list_ctx->stages_len) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Missing stages.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
blist_assure_user(ctx->handle, getuid(), getlogin());
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
blist_list *list =
|
||||||
|
blist_list_get_by_user_and_name(ctx->handle, user->id, list_ctx->name);
|
||||||
|
if (!list)
|
||||||
|
list = blist_list_init();
|
||||||
|
list->name = (unsigned char *)strdup(list_ctx->name);
|
||||||
|
if (list->desc)
|
||||||
|
list->desc = (unsigned char *)strdup(list_ctx->desc);
|
||||||
|
list->is_preset = false;
|
||||||
|
blist_list_save(ctx->handle, list, user);
|
||||||
|
printf("Added list '%s'\n", (char *)list->name);
|
||||||
|
int i = 0;
|
||||||
|
for (; i < list_ctx->stages_len; i++) {
|
||||||
|
blist_stage *stage = blist_stage_init();
|
||||||
|
stage->name = (unsigned char *)strdup(list_ctx->stages[i]);
|
||||||
|
stage->list_id = list->id;
|
||||||
|
blist_stage_save(ctx->handle, stage);
|
||||||
|
blist_stage_deinit(stage);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_list_edit(const blist_app *ctx) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Not implemented\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_list_get(const blist_app *ctx) {
|
||||||
|
blist_list_context *list_ctx = ctx->cmd_ctx;
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
|
||||||
|
size_t lists_len = 0;
|
||||||
|
blist_list **lists = NULL;
|
||||||
|
if (list_ctx->name) {
|
||||||
|
blist_list *list =
|
||||||
|
blist_list_get_by_user_and_name(ctx->handle, user->id, list_ctx->name);
|
||||||
|
if (list) {
|
||||||
|
lists_len = 1;
|
||||||
|
lists = malloc(lists_len * sizeof(blist_list));
|
||||||
|
memcpy(lists[0], list, sizeof(blist_list));
|
||||||
|
blist_list_deinit(list);
|
||||||
|
};
|
||||||
|
} else
|
||||||
|
lists = blist_list_get_all_by_user(ctx->handle, user->id, &lists_len);
|
||||||
|
|
||||||
|
if (lists_len) {
|
||||||
|
int i = 0;
|
||||||
|
for (; i < lists_len; i++) {
|
||||||
|
blist_list *list = lists[i];
|
||||||
|
size_t tasks_len = 0;
|
||||||
|
blist_task **tasks =
|
||||||
|
blist_task_get_for_list(ctx->handle, list->id, &tasks_len);
|
||||||
|
printf("Name: %s\n", (char *)list->name);
|
||||||
|
printf("Description: %s\n", list->desc ? (char *)list->desc : "N/A");
|
||||||
|
printf("Tasks: %ld\n", tasks_len);
|
||||||
|
int j = 0;
|
||||||
|
for (; j < tasks_len; j++)
|
||||||
|
blist_task_deinit(tasks[j]);
|
||||||
|
free(tasks);
|
||||||
|
blist_list_deinit(list);
|
||||||
|
}
|
||||||
|
free(lists);
|
||||||
|
} else if (list_ctx->name)
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "List '%s' not found.\n",
|
||||||
|
list_ctx->name);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_WARNING, "No lists found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_list_delete(const blist_app *ctx) {
|
||||||
|
blist_list_context *list_ctx = ctx->cmd_ctx;
|
||||||
|
blist_assure_user(ctx->handle, getuid(), getlogin());
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
if (!user) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "User doesn't exist.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list *list =
|
||||||
|
blist_list_get_by_user_and_name(ctx->handle, user->id, list_ctx->name);
|
||||||
|
if (list) {
|
||||||
|
size_t len = 0;
|
||||||
|
blist_task **tasks = blist_task_get_for_list(ctx->handle, list->id, &len);
|
||||||
|
int i = 0;
|
||||||
|
for (; i < len; i++) {
|
||||||
|
blist_task_delete(ctx->handle, tasks[i]);
|
||||||
|
blist_task_deinit(tasks[i]);
|
||||||
|
}
|
||||||
|
blist_stage **stages =
|
||||||
|
blist_stage_get_all_for_list(ctx->handle, list->id, &len);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
blist_stage_delete(ctx->handle, stages[i]);
|
||||||
|
blist_stage_deinit(stages[i]);
|
||||||
|
}
|
||||||
|
blist_list_delete(ctx->handle, list);
|
||||||
|
blist_list_deinit(list);
|
||||||
|
free(stages);
|
||||||
|
free(tasks);
|
||||||
|
} else {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "List '%s' not found.\n",
|
||||||
|
list_ctx->name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_task_add(const blist_app *ctx) {
|
||||||
|
return blist_app_task_edit(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_task_edit(const blist_app *ctx) {
|
||||||
|
blist_task_context *task_ctx = ctx->cmd_ctx;
|
||||||
|
blist_assure_user(ctx->handle, getuid(), getlogin());
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
blist_list *list = NULL;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "User doesn't exist.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
list = blist_list_get_by_user_and_name(ctx->handle, user->id, task_ctx->list);
|
||||||
|
if (list) {
|
||||||
|
size_t stages_len = 0;
|
||||||
|
int i = 0;
|
||||||
|
blist_stage **stages =
|
||||||
|
blist_stage_get_all_for_list(ctx->handle, list->id, &stages_len);
|
||||||
|
if (!task_ctx->stage) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"Stage missing/wrong, please pass one of: ");
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
fprintf(ctx->log_ctx->log_target, "%s ", (char *)stages[i]->name);
|
||||||
|
fprintf(ctx->log_ctx->log_target, "\n");
|
||||||
|
}
|
||||||
|
long stage_id = -1;
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
if (strcmp((char *)stages[i]->name, task_ctx->stage) == 0)
|
||||||
|
stage_id = stages[i]->id;
|
||||||
|
|
||||||
|
if (stage_id == -1) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"Stage %s not found. Use one of: ", task_ctx->stage);
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
fprintf(ctx->log_ctx->log_target, "%s ", (char *)stages[i]->name);
|
||||||
|
fprintf(ctx->log_ctx->log_target, "\n");
|
||||||
|
}
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
blist_stage_deinit(stages[i]);
|
||||||
|
free(stages);
|
||||||
|
|
||||||
|
blist_task *task =
|
||||||
|
blist_task_get_for_list_by_name(ctx->handle, list->id, task_ctx->name);
|
||||||
|
if (!task)
|
||||||
|
task = blist_task_init();
|
||||||
|
if (task->name)
|
||||||
|
free(task->name);
|
||||||
|
task->name = (unsigned char *)strdup(task_ctx->name);
|
||||||
|
if (task->desc)
|
||||||
|
free(task->desc);
|
||||||
|
if (task_ctx->desc)
|
||||||
|
task->desc = (unsigned char *)strdup(task_ctx->desc);
|
||||||
|
task->stage_id = stage_id;
|
||||||
|
blist_task_save(ctx->handle, task);
|
||||||
|
blist_task_deinit(task);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "List not found.\n");
|
||||||
|
blist_user_deinit(user);
|
||||||
|
if (list)
|
||||||
|
blist_list_deinit(list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_task_get(const blist_app *ctx) {
|
||||||
|
blist_task_context *task_ctx = ctx->cmd_ctx;
|
||||||
|
blist_assure_user(ctx->handle, getuid(), getlogin());
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
if (!user) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "User doesn't exist.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
blist_list *list =
|
||||||
|
blist_list_get_by_user_and_name(ctx->handle, user->id, task_ctx->list);
|
||||||
|
if (list) {
|
||||||
|
if (task_ctx->name) {
|
||||||
|
blist_task *task = blist_task_get_for_list_by_name(ctx->handle, list->id,
|
||||||
|
task_ctx->name);
|
||||||
|
if (task) {
|
||||||
|
printf("Name: %s\n", (char *)task->name);
|
||||||
|
printf("Description: %s\n", task->desc ? (char *)task->desc : "N/A");
|
||||||
|
blist_stage *stage = blist_stage_get_by_id(ctx->handle, task->stage_id);
|
||||||
|
printf("Stage: %s\n", (char *)stage->name);
|
||||||
|
blist_stage_deinit(stage);
|
||||||
|
blist_task_deinit(task);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Task not found.\n");
|
||||||
|
} else {
|
||||||
|
size_t tasks_len = 0;
|
||||||
|
blist_task **tasks =
|
||||||
|
blist_task_get_for_list(ctx->handle, list->id, &tasks_len);
|
||||||
|
blist_print_list(ctx->handle, list, tasks, tasks_len);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (; i < tasks_len; i++)
|
||||||
|
blist_task_deinit(tasks[i]);
|
||||||
|
free(tasks);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "list not found\n");
|
||||||
|
blist_user_deinit(user);
|
||||||
|
if (list)
|
||||||
|
blist_list_deinit(list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blist_app_task_delete(const blist_app *ctx) {
|
||||||
|
blist_task_context *task_ctx = ctx->cmd_ctx;
|
||||||
|
blist_assure_user(ctx->handle, getuid(), getlogin());
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx->handle, getuid());
|
||||||
|
if (!user) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "User doesn't exist.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
blist_list *list =
|
||||||
|
blist_list_get_by_user_and_name(ctx->handle, user->id, task_ctx->list);
|
||||||
|
if (list) {
|
||||||
|
if (task_ctx->name) {
|
||||||
|
blist_task *task = blist_task_get_for_list_by_name(ctx->handle, list->id,
|
||||||
|
task_ctx->name);
|
||||||
|
if (task) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_WARNING, "Deleting task '%s'.\n",
|
||||||
|
(char *)task->name);
|
||||||
|
blist_task_delete(ctx->handle, task);
|
||||||
|
blist_task_deinit(task);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Task not found.\n");
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"Task not found, only deletion by name is "
|
||||||
|
"currently supoprted.\n");
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Not implemented\n");
|
||||||
|
return 0;
|
||||||
|
}
|
85
src/cli/util.c
Normal file
85
src/cli/util.c
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
blist_app *blist_app_init(char *db) {
|
||||||
|
blist_app *app = malloc(sizeof(blist_app));
|
||||||
|
app->cmd = 0;
|
||||||
|
app->cmd_ctx = 0;
|
||||||
|
app->error = 0;
|
||||||
|
app->config = NULL;
|
||||||
|
app->log_ctx = malloc(sizeof(blist_logging_ctx));
|
||||||
|
app->log_ctx->log_level = BLIST_LOG_DEBUG;
|
||||||
|
app->log_ctx->log_target = stderr;
|
||||||
|
app->handle = blist_init(db);
|
||||||
|
app->handle->log_ctx->log_level = app->log_ctx->log_level;
|
||||||
|
app->handle->log_ctx->log_target = app->log_ctx->log_target;
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
void blist_app_deinit(blist_app *ctx) {
|
||||||
|
if (ctx->config)
|
||||||
|
free(ctx->config);
|
||||||
|
if (ctx->handle)
|
||||||
|
blist_deinit(ctx->handle);
|
||||||
|
else if (ctx->log_ctx)
|
||||||
|
fclose(ctx->log_ctx->log_target);
|
||||||
|
free(ctx->log_ctx);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_user_context *blist_user_context_init() {
|
||||||
|
blist_user_context *ctx = malloc(sizeof(blist_user_context));
|
||||||
|
ctx->cmd = -1;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
void blist_user_context_deinit(blist_user_context *ctx) {
|
||||||
|
free(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list_context *blist_list_context_init() {
|
||||||
|
blist_list_context *ctx = malloc(sizeof(blist_list_context));
|
||||||
|
ctx->cmd = -1;
|
||||||
|
ctx->name = NULL;
|
||||||
|
ctx->desc = NULL;
|
||||||
|
ctx->stages = NULL;
|
||||||
|
ctx->stages_len = 0;
|
||||||
|
ctx->preset = NULL;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
void blist_list_context_deinit(blist_list_context *ctx) {
|
||||||
|
if (ctx->name)
|
||||||
|
free(ctx->name);
|
||||||
|
if (ctx->desc)
|
||||||
|
free(ctx->desc);
|
||||||
|
if (ctx->preset)
|
||||||
|
free(ctx->preset);
|
||||||
|
if (ctx->stages) {
|
||||||
|
int i = 0;
|
||||||
|
for (; i < ctx->stages_len; i++)
|
||||||
|
free(ctx->stages[i]);
|
||||||
|
free(ctx->stages);
|
||||||
|
}
|
||||||
|
free(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task_context *blist_task_context_init() {
|
||||||
|
blist_task_context *ctx = malloc(sizeof(blist_task_context));
|
||||||
|
ctx->cmd = -1;
|
||||||
|
ctx->name = NULL;
|
||||||
|
ctx->desc = NULL;
|
||||||
|
ctx->list = NULL;
|
||||||
|
ctx->stage = NULL;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
void blist_task_context_deinit(blist_task_context *ctx) {
|
||||||
|
if (ctx->name)
|
||||||
|
free(ctx->name);
|
||||||
|
if (ctx->list)
|
||||||
|
free(ctx->list);
|
||||||
|
if (ctx->stage)
|
||||||
|
free(ctx->stage);
|
||||||
|
free(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
3
src/libblist/CMakeLists.txt
Normal file
3
src/libblist/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
add_library(libblist models.c sql.c util.c)
|
||||||
|
target_include_directories(libblist PUBLIC include)
|
||||||
|
target_link_libraries(libblist sqlite3)
|
8
src/libblist/include/blist.h
Normal file
8
src/libblist/include/blist.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef BLIST_H
|
||||||
|
#define BLIST_H
|
||||||
|
|
||||||
|
#include "models.h"
|
||||||
|
#include "sql.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#endif
|
78
src/libblist/include/models.h
Normal file
78
src/libblist/include/models.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct blist_user {
|
||||||
|
ssize_t id;
|
||||||
|
unsigned char *name;
|
||||||
|
ssize_t local_id;
|
||||||
|
};
|
||||||
|
typedef struct blist_user blist_user;
|
||||||
|
|
||||||
|
blist_user *blist_user_init();
|
||||||
|
blist_user *blist_user_init_from_sql(sqlite3_stmt *);
|
||||||
|
blist_user *blist_user_get_by_id(const blist *, u_int);
|
||||||
|
blist_user *blist_user_get_by_local(const blist *, u_int);
|
||||||
|
blist_user *blist_user_get_by_discord(const blist *, u_int);
|
||||||
|
blist_user *blist_user_get_by_google(const blist *, u_int);
|
||||||
|
void blist_user_save(const blist *, blist_user *);
|
||||||
|
void blist_user_delete(const blist *, const blist_user *);
|
||||||
|
void blist_user_deinit(blist_user *);
|
||||||
|
|
||||||
|
struct blist_list {
|
||||||
|
ssize_t id;
|
||||||
|
unsigned char *name;
|
||||||
|
unsigned char *desc;
|
||||||
|
bool is_preset;
|
||||||
|
};
|
||||||
|
typedef struct blist_list blist_list;
|
||||||
|
|
||||||
|
blist_list *blist_list_init();
|
||||||
|
blist_list *blist_list_init_from_sql(sqlite3_stmt *);
|
||||||
|
blist_list *blist_list_get_by_id(const blist *, u_int);
|
||||||
|
blist_list **blist_list_get_all_by_user(const blist *, u_int, size_t *);
|
||||||
|
blist_list *blist_list_get_by_user_and_name(const blist *, u_int, const char *);
|
||||||
|
void blist_list_save(const blist *, blist_list *, const blist_user *);
|
||||||
|
void blist_list_delete(const blist *, blist_list *);
|
||||||
|
void blist_list_deinit(blist_list *);
|
||||||
|
|
||||||
|
struct blist_stage {
|
||||||
|
ssize_t id;
|
||||||
|
unsigned char *name;
|
||||||
|
unsigned char *desc;
|
||||||
|
ssize_t list_id;
|
||||||
|
};
|
||||||
|
typedef struct blist_stage blist_stage;
|
||||||
|
|
||||||
|
blist_stage *blist_stage_init();
|
||||||
|
blist_stage *blist_stage_init_from_sql(sqlite3_stmt *);
|
||||||
|
blist_stage *blist_stage_get_by_id(const blist *, u_int);
|
||||||
|
blist_stage **blist_stage_get_all_for_list(const blist *, u_int, size_t *);
|
||||||
|
void blist_stage_save(const blist *, blist_stage *);
|
||||||
|
void blist_stage_delete(const blist *, const blist_stage *);
|
||||||
|
void blist_stage_deinit(blist_stage *);
|
||||||
|
|
||||||
|
struct blist_task {
|
||||||
|
ssize_t id;
|
||||||
|
unsigned char *name;
|
||||||
|
unsigned char *desc;
|
||||||
|
int stage_id;
|
||||||
|
time_t due;
|
||||||
|
int target_stage;
|
||||||
|
};
|
||||||
|
typedef struct blist_task blist_task;
|
||||||
|
|
||||||
|
blist_task *blist_task_init();
|
||||||
|
blist_task *blist_task_init_from_sql(sqlite3_stmt *);
|
||||||
|
blist_task *blist_task_get_by_id(const blist *, u_int);
|
||||||
|
blist_task **blist_task_get_for_list(const blist *, u_int, size_t *);
|
||||||
|
blist_task *blist_task_get_for_list_by_name(const blist *, u_int, const char *);
|
||||||
|
void blist_task_save(const blist *, blist_task *);
|
||||||
|
void blist_task_delete(const blist *, const blist_task *);
|
||||||
|
void blist_task_deinit(blist_task *);
|
||||||
|
|
||||||
|
void blist_print_user(blist *, const blist_user *);
|
||||||
|
void blist_print_list(const blist *, const blist_list *, blist_task **, size_t);
|
||||||
|
void blist_print_task(blist *, blist_task **, size_t);
|
35
src/libblist/include/sql.h
Normal file
35
src/libblist/include/sql.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum BLIST_SQL {
|
||||||
|
INIT,
|
||||||
|
ADD_LOGIN,
|
||||||
|
ADD_USER,
|
||||||
|
GET_USER,
|
||||||
|
GET_USER_BY_LOCAL,
|
||||||
|
GET_USERS,
|
||||||
|
MOD_USER_NAME,
|
||||||
|
DEL_USER,
|
||||||
|
ADD_LIST,
|
||||||
|
ADD_USER_LIST,
|
||||||
|
GET_LIST,
|
||||||
|
GET_LISTS,
|
||||||
|
GET_LISTS_BY_USER,
|
||||||
|
GET_LISTS_BY_USER_NAME,
|
||||||
|
MOD_LIST_NAME,
|
||||||
|
DEL_LIST,
|
||||||
|
ADD_STAGE,
|
||||||
|
GET_STAGE,
|
||||||
|
GET_STAGES_FOR_LIST,
|
||||||
|
DEL_STAGE,
|
||||||
|
ADD_TASK,
|
||||||
|
GET_TASK,
|
||||||
|
GET_TASKS,
|
||||||
|
GET_TASKS_FOR_LIST,
|
||||||
|
GET_TASKS_FOR_LIST_BY_NAME,
|
||||||
|
MOD_TASK_NAME,
|
||||||
|
MOD_TASK_DESCRIPTION,
|
||||||
|
DEL_TASK,
|
||||||
|
_BLIST_SQL_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *blist_sql_get(const enum BLIST_SQL);
|
57
src/libblist/include/util.h
Normal file
57
src/libblist/include/util.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#pragma once
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* command parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum BLIST_LOG_LEVEL {
|
||||||
|
BLIST_LOG_ERROR,
|
||||||
|
BLIST_LOG_WARNING,
|
||||||
|
BLIST_LOG_INFO,
|
||||||
|
BLIST_LOG_DEBUG
|
||||||
|
};
|
||||||
|
typedef enum BLIST_LOG_LEVEL BLIST_LOG_LEVEL;
|
||||||
|
|
||||||
|
struct blist_logging_ctx {
|
||||||
|
BLIST_LOG_LEVEL log_level;
|
||||||
|
FILE *log_target;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct blist_logging_ctx blist_logging_ctx;
|
||||||
|
|
||||||
|
struct blist {
|
||||||
|
sqlite3 *db;
|
||||||
|
sqlite3_stmt **stmts;
|
||||||
|
int error;
|
||||||
|
blist_logging_ctx *log_ctx;
|
||||||
|
};
|
||||||
|
typedef struct blist blist;
|
||||||
|
|
||||||
|
blist *blist_init(char *db);
|
||||||
|
void blist_deinit(blist *list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sql preparations
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blist_sql_prepare(blist *, char *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* assuring things
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool blist_assure_user(const blist *, __uid_t, const char *);
|
||||||
|
void blist_assure_list(blist *, char *);
|
||||||
|
void blist_assure_task(blist *, char *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* printing methods
|
||||||
|
*
|
||||||
|
* json printing will be implemented eventually
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blist_print(blist_logging_ctx *, BLIST_LOG_LEVEL, const char *, ...);
|
546
src/libblist/models.c
Normal file
546
src/libblist/models.c
Normal file
|
@ -0,0 +1,546 @@
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "blist.h"
|
||||||
|
#include "sql.h"
|
||||||
|
|
||||||
|
blist_user *blist_user_init() {
|
||||||
|
blist_user *user = malloc(sizeof(blist_user));
|
||||||
|
user->id = -1;
|
||||||
|
user->name = NULL;
|
||||||
|
user->local_id = -1;
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_user *blist_user_init_from_sql(sqlite3_stmt *stmt) {
|
||||||
|
blist_user *user = blist_user_init();
|
||||||
|
user->id = sqlite3_column_int(stmt, 0);
|
||||||
|
if (sqlite3_column_bytes(stmt, 1) > 0)
|
||||||
|
user->name = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 1));
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_user *blist_user_get_by_id(const blist *ctx, u_int id) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_USER];
|
||||||
|
blist_user *user = NULL;
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
|
return blist_user_init_from_sql(stmt);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_user *blist_user_get_by_local(const blist *ctx, u_int local_id) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_USER_BY_LOCAL];
|
||||||
|
blist_user *user = NULL;
|
||||||
|
sqlite3_bind_int(stmt, 1, local_id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
user = blist_user_init_from_sql(stmt);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"failed to get user by local id %u: %s\n", local_id,
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_user_save(const blist *ctx, blist_user *user) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[ADD_USER];
|
||||||
|
int result = 0;
|
||||||
|
if (user->id > -1)
|
||||||
|
sqlite3_bind_int(stmt, 1, user->id);
|
||||||
|
sqlite3_bind_text(stmt, 2, (char *)user->name, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
if ((result = sqlite3_step(stmt)) == SQLITE_ROW)
|
||||||
|
user->id = sqlite3_column_int(stmt, 0);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to save user: %s - %d\n",
|
||||||
|
sqlite3_errmsg(ctx->db), result);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
|
||||||
|
if (user->local_id != -1) {
|
||||||
|
stmt = ctx->stmts[ADD_LOGIN];
|
||||||
|
sqlite3_bind_int(stmt, 1, user->id);
|
||||||
|
sqlite3_bind_int(stmt, 2, user->local_id);
|
||||||
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"failed to save logins for user: %s - %d\n",
|
||||||
|
sqlite3_errmsg(ctx->db), result);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "no local uid saved for user %s",
|
||||||
|
(char *)user->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_user_delete(const blist *ctx, const blist_user *user) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[DEL_USER];
|
||||||
|
sqlite3_bind_int(stmt, 1, (int)user->id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_user_deinit(blist_user *user) {
|
||||||
|
if (user->name)
|
||||||
|
free(user->name);
|
||||||
|
free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_stage *blist_stage_init() {
|
||||||
|
blist_stage *stage = malloc(sizeof(blist_stage));
|
||||||
|
stage->id = -1;
|
||||||
|
stage->name = NULL;
|
||||||
|
stage->desc = NULL;
|
||||||
|
stage->list_id = -1;
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_stage *blist_stage_init_from_sql(sqlite3_stmt *stmt) {
|
||||||
|
blist_stage *stage = blist_stage_init();
|
||||||
|
stage->id = sqlite3_column_int(stmt, 0);
|
||||||
|
if (sqlite3_column_bytes(stmt, 1) > 0)
|
||||||
|
stage->name = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 1));
|
||||||
|
if (sqlite3_column_bytes(stmt, 2) > 0)
|
||||||
|
stage->desc = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 2));
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_stage *blist_stage_get_by_id(const blist *ctx, u_int id) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_STAGE];
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return blist_stage_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_stage **blist_stage_get_all_for_list(const blist *ctx, u_int id,
|
||||||
|
size_t *count) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_STAGES_FOR_LIST];
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
blist_stage **stages = NULL;
|
||||||
|
size_t stages_len = 0;
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
blist_stage **_stages =
|
||||||
|
realloc(stages, ++stages_len * sizeof(blist_stage *));
|
||||||
|
if (_stages) {
|
||||||
|
stages = _stages;
|
||||||
|
stages[stages_len - 1] = blist_stage_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count != NULL)
|
||||||
|
*count = stages_len;
|
||||||
|
return stages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_stage_save(const blist *ctx, blist_stage *stage) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[ADD_STAGE];
|
||||||
|
int result = 0;
|
||||||
|
if (stage->id > -1)
|
||||||
|
sqlite3_bind_int(stmt, 1, stage->id);
|
||||||
|
sqlite3_bind_text(stmt, 2, (char *)stage->name, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 3, (char *)stage->name, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_int(stmt, 4, stage->list_id);
|
||||||
|
if ((result = sqlite3_step(stmt)) == SQLITE_ROW || result == SQLITE_DONE) {
|
||||||
|
stage->id = sqlite3_column_int(stmt, 0);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"failed to save stage: %s - %d\n", sqlite3_errmsg(ctx->db),
|
||||||
|
result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_stage_delete(const blist *ctx, const blist_stage *stage) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[DEL_STAGE];
|
||||||
|
sqlite3_bind_int(stmt, 1, stage->id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_DONE)
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to delete task: %s\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_stage_deinit(blist_stage *stage) {
|
||||||
|
if (stage->name)
|
||||||
|
free(stage->name);
|
||||||
|
if (stage->desc)
|
||||||
|
free(stage->desc);
|
||||||
|
free(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list *blist_list_init() {
|
||||||
|
blist_list *list = malloc(sizeof(blist_list));
|
||||||
|
list->id = -1;
|
||||||
|
list->name = NULL;
|
||||||
|
list->desc = NULL;
|
||||||
|
list->is_preset = false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list *blist_list_init_from_sql(sqlite3_stmt *stmt) {
|
||||||
|
blist_list *list = blist_list_init();
|
||||||
|
list->id = sqlite3_column_int(stmt, 0);
|
||||||
|
if (sqlite3_column_bytes(stmt, 1) > 0)
|
||||||
|
list->name = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 1));
|
||||||
|
if (sqlite3_column_bytes(stmt, 2) > 0)
|
||||||
|
list->desc = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 2));
|
||||||
|
|
||||||
|
list->is_preset = sqlite3_column_int(stmt, 3) ? true : false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list *blist_list_get_by_id(const blist *ctx, u_int id) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_LIST];
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return blist_list_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list **blist_list_get_all_by_user(const blist *ctx, u_int user_id,
|
||||||
|
size_t *count) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_LISTS_BY_USER];
|
||||||
|
sqlite3_bind_int(stmt, 1, user_id);
|
||||||
|
blist_list **lists = NULL;
|
||||||
|
size_t lists_len = 0;
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
blist_list **_lists = realloc(lists, ++lists_len * sizeof(blist_list *));
|
||||||
|
if (_lists) {
|
||||||
|
lists = _lists;
|
||||||
|
lists[lists_len - 1] = blist_list_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count != NULL)
|
||||||
|
*count = lists_len;
|
||||||
|
return lists;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_list *blist_list_get_by_user_and_name(const blist *ctx, u_int user_id,
|
||||||
|
const char *name) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_LISTS_BY_USER_NAME];
|
||||||
|
blist_list *list = NULL;
|
||||||
|
sqlite3_bind_int(stmt, 1, user_id);
|
||||||
|
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
|
list = blist_list_init_from_sql(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"failed to get lists for user: %s\n", sqlite3_errmsg(ctx->db));
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_list_save(const blist *ctx, blist_list *list,
|
||||||
|
const blist_user *user) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[ADD_LIST];
|
||||||
|
int result = 0;
|
||||||
|
if (list->id > -1)
|
||||||
|
sqlite3_bind_int(stmt, 1, list->id);
|
||||||
|
sqlite3_bind_text(stmt, 2, (char *)list->name, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 3, (char *)list->desc, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_int(stmt, 4, list->is_preset);
|
||||||
|
if ((result = sqlite3_step(stmt)) == SQLITE_ROW)
|
||||||
|
list->id = sqlite3_column_int(stmt, 0);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to save list: %s - %d\n",
|
||||||
|
sqlite3_errmsg(ctx->db), result);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
|
||||||
|
if (list->id > -1) {
|
||||||
|
stmt = ctx->stmts[ADD_USER_LIST];
|
||||||
|
sqlite3_bind_int(stmt, 1, user->id);
|
||||||
|
sqlite3_bind_int(stmt, 2, list->id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_DONE)
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"failed to save user - list relation in join table: %s\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_list_delete(const blist *ctx, blist_list *list) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[DEL_LIST];
|
||||||
|
sqlite3_bind_int(stmt, 1, list->id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_DONE)
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to delete task: %s\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_list_deinit(blist_list *list) {
|
||||||
|
if (list->name)
|
||||||
|
free(list->name);
|
||||||
|
if (list->desc)
|
||||||
|
free(list->desc);
|
||||||
|
free(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task *blist_task_init() {
|
||||||
|
blist_task *task = malloc(sizeof(blist_task));
|
||||||
|
task->id = -1;
|
||||||
|
task->name = NULL;
|
||||||
|
task->desc = NULL;
|
||||||
|
task->due = -1;
|
||||||
|
task->target_stage = -1;
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task *blist_task_init_from_sql(sqlite3_stmt *stmt) {
|
||||||
|
blist_task *task = blist_task_init();
|
||||||
|
task->id = sqlite3_column_int(stmt, 0);
|
||||||
|
if (sqlite3_column_bytes(stmt, 1) > 0)
|
||||||
|
task->name = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 1));
|
||||||
|
if (sqlite3_column_bytes(stmt, 2) > 0)
|
||||||
|
task->desc = (unsigned char *)strdup((char *)sqlite3_column_text(stmt, 2));
|
||||||
|
task->stage_id = sqlite3_column_int(stmt, 3);
|
||||||
|
task->due = sqlite3_column_int(stmt, 4);
|
||||||
|
task->target_stage = sqlite3_column_int(stmt, 5);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task *blist_task_get_by_id(const blist *ctx, u_int id) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_TASK];
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return blist_task_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task **blist_task_get_for_list(const blist *ctx, u_int list_id,
|
||||||
|
size_t *count) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_TASKS_FOR_LIST];
|
||||||
|
sqlite3_bind_int(stmt, 1, list_id);
|
||||||
|
blist_task **tasks = NULL;
|
||||||
|
size_t tasks_len = 0;
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
tasks_len++;
|
||||||
|
blist_task **_tasks = realloc(tasks, tasks_len * sizeof(blist_task *));
|
||||||
|
if (_tasks) {
|
||||||
|
tasks = _tasks;
|
||||||
|
tasks[tasks_len - 1] = blist_task_init_from_sql(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
*count = tasks_len;
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
blist_task *blist_task_get_for_list_by_name(const blist *ctx, u_int list_id,
|
||||||
|
const char *name) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[GET_TASKS_FOR_LIST_BY_NAME];
|
||||||
|
blist_task *task = NULL;
|
||||||
|
sqlite3_bind_int(stmt, 1, list_id);
|
||||||
|
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
|
task = blist_task_init_from_sql(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_WARNING,
|
||||||
|
"no tasks found for list %d, name '%s': %s\n", list_id, name,
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_task_save(const blist *ctx, blist_task *task) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[ADD_TASK];
|
||||||
|
int result = 0;
|
||||||
|
if (task->id > -1)
|
||||||
|
sqlite3_bind_int(stmt, 1, task->id);
|
||||||
|
sqlite3_bind_text(stmt, 2, (char *)task->name, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 3, (char *)task->desc, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_int(stmt, 4, task->stage_id);
|
||||||
|
if (task->due > 0)
|
||||||
|
sqlite3_bind_int(stmt, 5, task->due);
|
||||||
|
if (task->target_stage > 0)
|
||||||
|
sqlite3_bind_int(stmt, 6, task->target_stage);
|
||||||
|
if ((result = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||||
|
task->id = sqlite3_column_int(stmt, 0);
|
||||||
|
} else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to save list: %s - %d\n",
|
||||||
|
sqlite3_errmsg(ctx->db), result);
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
}
|
||||||
|
void blist_task_delete(const blist *ctx, const blist_task *task) {
|
||||||
|
sqlite3_stmt *stmt = ctx->stmts[DEL_TASK];
|
||||||
|
sqlite3_bind_int(stmt, 1, task->id);
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_DONE)
|
||||||
|
sqlite3_reset(stmt);
|
||||||
|
else
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "failed to delete task: %s\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_task_deinit(blist_task *task) {
|
||||||
|
if (task->name)
|
||||||
|
free(task->name);
|
||||||
|
if (task->desc)
|
||||||
|
free(task->desc);
|
||||||
|
free(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* printing
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blist_print_user(blist *ctx, const blist_user *user) {
|
||||||
|
fprintf(ctx->log_ctx->log_target,
|
||||||
|
""
|
||||||
|
"Name: %s\n"
|
||||||
|
"has local account: %s\n",
|
||||||
|
(char *)user->name, user->local_id > 0 ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_table_line(char *left, size_t cols, char *filler, size_t col_width,
|
||||||
|
char *col_delim, char *right) {
|
||||||
|
printf("%s", left);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < cols; i++) {
|
||||||
|
int j = 0;
|
||||||
|
for (; j < col_width; j++)
|
||||||
|
printf("%s", filler);
|
||||||
|
if (i + 1 != cols)
|
||||||
|
printf("%s%s", filler, col_delim);
|
||||||
|
}
|
||||||
|
printf("%s\n", right);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_print_list(const blist *ctx, const blist_list *list,
|
||||||
|
blist_task **tasks, size_t tasks_len) {
|
||||||
|
struct winsize w;
|
||||||
|
int i = 0;
|
||||||
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||||
|
u_int width = w.ws_col > 0 ? w.ws_col : 40;
|
||||||
|
for (i = 0; i < tasks_len; i++)
|
||||||
|
fprintf(stderr, "%d, %s\n", i, (char *)tasks[i]->name);
|
||||||
|
|
||||||
|
size_t stages_len = 0;
|
||||||
|
blist_stage **stages =
|
||||||
|
blist_stage_get_all_for_list(ctx, list->id, &stages_len);
|
||||||
|
|
||||||
|
int max_stages_id = 0;
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
if (stages[i]->id > max_stages_id)
|
||||||
|
max_stages_id = stages[i]->id;
|
||||||
|
|
||||||
|
int tasks_count_per_stage[max_stages_id + 1];
|
||||||
|
for (i = 0; i < max_stages_id + 1; i++)
|
||||||
|
tasks_count_per_stage[i] = 0;
|
||||||
|
|
||||||
|
u_long task_length = 0, max_task_length = 0;
|
||||||
|
for (i = 0; i < tasks_len; i++) {
|
||||||
|
tasks_count_per_stage[tasks[i]->stage_id]++;
|
||||||
|
task_length = strlen((char *)tasks[i]->name);
|
||||||
|
if (task_length > max_task_length)
|
||||||
|
max_task_length = task_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int content_width =
|
||||||
|
(int)((double)max_task_length * 1.6 * (double)stages_len);
|
||||||
|
if (content_width)
|
||||||
|
width = content_width;
|
||||||
|
size_t stage_col_width = (width - 3 - (stages_len - 1)) / stages_len;
|
||||||
|
|
||||||
|
int tasks_max = 0;
|
||||||
|
for (i = 0; i < max_stages_id + 1; i++)
|
||||||
|
if (tasks_count_per_stage[i] > tasks_max)
|
||||||
|
tasks_max = tasks_count_per_stage[i];
|
||||||
|
|
||||||
|
print_table_line("╭", stages_len, "─", stage_col_width, "─", "╮");
|
||||||
|
|
||||||
|
// list name
|
||||||
|
printf("│");
|
||||||
|
printf(" %s", (char *)list->name);
|
||||||
|
for (i = 0;
|
||||||
|
i < (stages_len * stage_col_width) - (strlen((char *)list->name + 1));
|
||||||
|
i++)
|
||||||
|
printf(" ");
|
||||||
|
printf("│\n");
|
||||||
|
|
||||||
|
// separator
|
||||||
|
print_table_line("├", stages_len, "─", stage_col_width, "┬", "┤");
|
||||||
|
|
||||||
|
// stage name
|
||||||
|
printf("│");
|
||||||
|
for (i = 0; i < stages_len; i++) {
|
||||||
|
int j = 0;
|
||||||
|
printf(" %s", (char *)stages[i]->name);
|
||||||
|
for (; j < (stage_col_width - 1) - strlen((char *)stages[i]->name); j++)
|
||||||
|
printf(" ");
|
||||||
|
if (i + 1 != stages_len)
|
||||||
|
printf(" │");
|
||||||
|
}
|
||||||
|
printf("│\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
const char *crossings[] = { "┬", "┼", "┴" };
|
||||||
|
const char *headers[] = { "Name", "due" };
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
printf("%s", (i + 1) % 2 ? "├" : "│");
|
||||||
|
int j = 0;
|
||||||
|
for (j = 0; j < stages_len * 2; j++) {
|
||||||
|
int k = 0, printed = 0;
|
||||||
|
if (i == 1) {
|
||||||
|
printf(" %s", headers[j % 2]);
|
||||||
|
printed = strlen(headers[j % 2]) + 1;
|
||||||
|
}
|
||||||
|
for (; k < ((stage_col_width / 2) - 1) - printed; k++)
|
||||||
|
printf("%s", (i + 1) % 2 ? "─" : " ");
|
||||||
|
if (j + 1 != stages_len * 2)
|
||||||
|
if ((i + 1) % 2)
|
||||||
|
printf("─%s", j % 2 ? crossings[1] : crossings[i > 1]);
|
||||||
|
else
|
||||||
|
printf(" │");
|
||||||
|
}
|
||||||
|
printf("%s\n", (i + 1) % 2 ? "┤" : "│");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// separator
|
||||||
|
if (tasks_len)
|
||||||
|
print_table_line("├", stages_len, "─", stage_col_width, "┼", "┤");
|
||||||
|
|
||||||
|
// tasks
|
||||||
|
int stages_idx[stages_len];
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
stages_idx[i] = 0;
|
||||||
|
for (i = 0; i < tasks_max; i++) {
|
||||||
|
printf("│");
|
||||||
|
int j = 0;
|
||||||
|
for (; j < stages_len; j++) {
|
||||||
|
int k = stages_idx[j];
|
||||||
|
u_long printed = 0;
|
||||||
|
for (; k < tasks_len; k++)
|
||||||
|
if (tasks[k]->stage_id == stages[j]->id) {
|
||||||
|
printed = printf(" %s", (char *)tasks[k]->name);
|
||||||
|
stages_idx[j] = k + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int l = 0;
|
||||||
|
for (; l < stage_col_width - printed; l++)
|
||||||
|
printf(" ");
|
||||||
|
if (j + 1 != stages_len)
|
||||||
|
printf(" │");
|
||||||
|
}
|
||||||
|
printf("│\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// end line
|
||||||
|
print_table_line("╰", stages_len, "─", stage_col_width, "┴", "╯");
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
for (i = 0; i < stages_len; i++)
|
||||||
|
blist_stage_deinit(stages[i]);
|
||||||
|
free(stages);
|
||||||
|
}
|
130
src/libblist/sql.c
Normal file
130
src/libblist/sql.c
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#include "include/sql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: update queries so that upserts actually can be used as such.
|
||||||
|
* details: ids are mostly missing in the insert statements meaning that there
|
||||||
|
* can be no id conflicts
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *blist_sql_get(const enum BLIST_SQL sql) {
|
||||||
|
switch (sql) {
|
||||||
|
case INIT:
|
||||||
|
return "create table if not exists users (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" name text not null\n"
|
||||||
|
");\n"
|
||||||
|
"create table if not exists user_logins (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" user_id integer not null,\n"
|
||||||
|
" local_id integer,\n"
|
||||||
|
" foreign key (user_id) references users(id) on delete cascade,\n"
|
||||||
|
" unique(user_id, local_id) on conflict ignore,\n"
|
||||||
|
" unique(user_id)\n"
|
||||||
|
");\n"
|
||||||
|
"create table if not exists user_lists (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" user_id integer not null,\n"
|
||||||
|
" list_id integer not null,\n"
|
||||||
|
" foreign key (user_id) references users(id) on delete cascade,\n"
|
||||||
|
" foreign key (list_id) references lists(id) on delete cascade\n"
|
||||||
|
");\n"
|
||||||
|
"create table if not exists lists (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" name text not null,\n"
|
||||||
|
" desc text,\n"
|
||||||
|
" is_preset int not null\n"
|
||||||
|
");\n"
|
||||||
|
"create table if not exists task_stages (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" name text not null,\n"
|
||||||
|
" desc text,\n"
|
||||||
|
" list_id integer not null,\n"
|
||||||
|
" foreign key (list_id) references lists(id)\n"
|
||||||
|
");\n"
|
||||||
|
"create table if not exists tasks (\n"
|
||||||
|
" id integer primary key,\n"
|
||||||
|
" name text not null,\n"
|
||||||
|
" desc text,\n"
|
||||||
|
" stage integer not null,\n"
|
||||||
|
" due date,\n"
|
||||||
|
" target_stage integer, -- more like target stage\n"
|
||||||
|
" foreign key (stage) references task_stages(id),\n"
|
||||||
|
" foreign key (target_stage) references task_stages(id)\n"
|
||||||
|
")";
|
||||||
|
case ADD_LOGIN:
|
||||||
|
return "insert into user_logins (user_id, local_id) values (?1, ?2) on "
|
||||||
|
"conflict (user_id) do update set local_id=excluded.local_id "
|
||||||
|
"returning id";
|
||||||
|
case ADD_USER:
|
||||||
|
return "insert into users (id, name) values (?1, ?2) on conflict (id) do "
|
||||||
|
"update "
|
||||||
|
"set name=excluded.name returning id";
|
||||||
|
case GET_USER:
|
||||||
|
return "select users.*, user_logins.local_id from users inner join "
|
||||||
|
"user_logins on user_logins.user_id = users.id where users.id = ?1";
|
||||||
|
case GET_USER_BY_LOCAL:
|
||||||
|
return "select users.* from users inner join user_logins on "
|
||||||
|
"user_logins.user_id "
|
||||||
|
"= users.id where user_logins.local_id = ?1";
|
||||||
|
case GET_USERS:
|
||||||
|
return "select * from users";
|
||||||
|
case MOD_USER_NAME:
|
||||||
|
return "update users set name = ?1 where id = ?2";
|
||||||
|
case DEL_USER:
|
||||||
|
return "delete from users where id = ?1";
|
||||||
|
case ADD_LIST:
|
||||||
|
return "insert into lists (id, name, desc, is_preset) values (?1, ?2, ?3, "
|
||||||
|
"?4) on conflict (id) do update set name=excluded.name, "
|
||||||
|
"desc=excluded.desc, is_preset=excluded.is_preset returning id";
|
||||||
|
case ADD_USER_LIST:
|
||||||
|
return "insert into user_lists (user_id, list_id) values (?1, ?2)";
|
||||||
|
case GET_LIST:
|
||||||
|
return "select * from lists where id = ?1";
|
||||||
|
case GET_LISTS:
|
||||||
|
return "select * from lists";
|
||||||
|
case GET_LISTS_BY_USER:
|
||||||
|
return "select * from lists inner join user_lists on user_lists.list_id = "
|
||||||
|
"lists.id where user_lists.user_id = ?1";
|
||||||
|
case GET_LISTS_BY_USER_NAME:
|
||||||
|
return "select * from lists inner join user_lists on user_lists.list_id = "
|
||||||
|
"lists.id where user_lists.user_id = ?1 and lists.name like ?2";
|
||||||
|
case MOD_LIST_NAME:
|
||||||
|
return "update lists set name = ?1 where id = ?2";
|
||||||
|
case DEL_LIST:
|
||||||
|
return "delete from lists where id = ?1";
|
||||||
|
case ADD_STAGE:
|
||||||
|
return "insert into task_stages (id, name, desc, list_id) values (?1, ?2, "
|
||||||
|
"?3, ?4) on conflict (id) do update set name=excluded.name, "
|
||||||
|
"desc=excluded.desc returning id";
|
||||||
|
case GET_STAGE:
|
||||||
|
return "select * from task_stages where id = ?1";
|
||||||
|
case GET_STAGES_FOR_LIST:
|
||||||
|
return "select * from task_stages where list_id = ?1";
|
||||||
|
case DEL_STAGE:
|
||||||
|
return "delete from task_stages where id = ?1";
|
||||||
|
case ADD_TASK:
|
||||||
|
return "insert into tasks (id, name, desc, stage, due, target_stage) "
|
||||||
|
"values (?1, ?2, ?3, ?4, ?5, ?6) on conflict (id) do update set "
|
||||||
|
"name=excluded.name, desc=excluded.desc, stage=excluded.stage, "
|
||||||
|
"due=excluded.due, target_stage=excluded.target_stage returning id";
|
||||||
|
case GET_TASK:
|
||||||
|
return "select * from tasks where id = ?1";
|
||||||
|
case GET_TASKS:
|
||||||
|
return "select * from tasks;";
|
||||||
|
case GET_TASKS_FOR_LIST:
|
||||||
|
return "select tasks.* from tasks join task_stages on task_stages.id = "
|
||||||
|
"tasks.stage where task_stages.list_id = ?1";
|
||||||
|
case GET_TASKS_FOR_LIST_BY_NAME:
|
||||||
|
return "select tasks.* from tasks join task_stages on task_stages.id = "
|
||||||
|
"tasks.stage where task_stages.list_id = ?1 and tasks.name = ?2";
|
||||||
|
case MOD_TASK_NAME:
|
||||||
|
return "update tasks set name = ?1 where id = ?2";
|
||||||
|
case MOD_TASK_DESCRIPTION:
|
||||||
|
return "update tasks set desc = ?1 where id = ?2";
|
||||||
|
case DEL_TASK:
|
||||||
|
return "delete from tasks where id = ?1";
|
||||||
|
case _BLIST_SQL_COUNT:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
117
src/libblist/util.c
Normal file
117
src/libblist/util.c
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "models.h"
|
||||||
|
#include "sql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* command parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
blist *blist_init(char *db) {
|
||||||
|
blist *ctx = malloc(sizeof(blist));
|
||||||
|
ctx->stmts = malloc(_BLIST_SQL_COUNT * sizeof(sqlite3_stmt *));
|
||||||
|
ctx->error = 0;
|
||||||
|
ctx->log_ctx = malloc(sizeof(blist_logging_ctx));
|
||||||
|
blist_sql_prepare(ctx, db);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
void blist_deinit(blist *ctx) {
|
||||||
|
int i = 0;
|
||||||
|
for (; i < _BLIST_SQL_COUNT; i++)
|
||||||
|
sqlite3_finalize(ctx->stmts[i]);
|
||||||
|
sqlite3_close(ctx->db);
|
||||||
|
if (ctx->log_ctx) {
|
||||||
|
if (ctx->log_ctx->log_target)
|
||||||
|
fclose(ctx->log_ctx->log_target);
|
||||||
|
free(ctx->log_ctx);
|
||||||
|
}
|
||||||
|
free(ctx->stmts);
|
||||||
|
free(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sql preparations
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blist_sql_prepare(blist *ctx, char *db) {
|
||||||
|
if (!db)
|
||||||
|
blist_print(
|
||||||
|
ctx->log_ctx, BLIST_LOG_WARNING,
|
||||||
|
"Database will be in-memory only, changes are not persistent!\n");
|
||||||
|
if (sqlite3_open(db ? db : ":memory:", &ctx->db) != SQLITE_OK) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR, "Can't open database: %s\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *errmsg = NULL;
|
||||||
|
sqlite3_exec(ctx->db, blist_sql_get(INIT), NULL, NULL, &errmsg);
|
||||||
|
if (errmsg) {
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_ERROR,
|
||||||
|
"Database initialization failed (%s), expect issues.\n",
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
sqlite3_free(errmsg);
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
for (; i < _BLIST_SQL_COUNT; i++)
|
||||||
|
if (sqlite3_prepare(ctx->db, blist_sql_get(i), -1, &ctx->stmts[i], NULL) !=
|
||||||
|
SQLITE_OK)
|
||||||
|
blist_print(ctx->log_ctx, BLIST_LOG_WARNING,
|
||||||
|
"sqlite3_prepare for '%s' failed: %s\n", blist_sql_get(i),
|
||||||
|
sqlite3_errmsg(ctx->db));
|
||||||
|
}
|
||||||
|
|
||||||
|
void blist_sql_seed(blist *ctx) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* assuring things
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool blist_assure_user(const blist *ctx, const __uid_t id, const char *name) {
|
||||||
|
blist_user *user = blist_user_get_by_local(ctx, id);
|
||||||
|
bool created = false;
|
||||||
|
if (!user) {
|
||||||
|
user = blist_user_init();
|
||||||
|
user->name = malloc((strlen(name) + 1) * sizeof(char));
|
||||||
|
strcpy((char *)user->name, name);
|
||||||
|
user->local_id = id;
|
||||||
|
blist_user_save(ctx, user);
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
blist_user_deinit(user);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* printing methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blist_print(blist_logging_ctx *ctx, BLIST_LOG_LEVEL log_level,
|
||||||
|
const char *format, ...) {
|
||||||
|
if (ctx->log_level >= log_level) {
|
||||||
|
switch (log_level) {
|
||||||
|
case BLIST_LOG_DEBUG:
|
||||||
|
fprintf(ctx->log_target, "[\x1B[34mDBG\x1B[0m] ");
|
||||||
|
break;
|
||||||
|
case BLIST_LOG_INFO:
|
||||||
|
fprintf(ctx->log_target, "[\x1B[36mINF\x1B[0m] ");
|
||||||
|
break;
|
||||||
|
case BLIST_LOG_WARNING:
|
||||||
|
fprintf(ctx->log_target, "[\x1B[33mWRN\x1B[0m] ");
|
||||||
|
break;
|
||||||
|
case BLIST_LOG_ERROR:
|
||||||
|
fprintf(ctx->log_target, "[\x1B[31mERR\x1B[0m] ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vprintf(format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue