aisl-sdk/components/todo.c

384 lines
6.5 KiB
C
Raw Normal View History

2019-06-24 07:42:56 +02:00
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file todo.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/
#include <stdlib.h>
#include <string.h>
#include <uuid.h>
#include <errno.h>
#include <cStuff/string.h>
#include <components/log.h>
#include "todo.h"
AislStatus
ax_todo_init(AxTodo self, const char *file)
{
FILE *f;
uuid_generate(self->id);
if (!(f = fopen(file, "a+"))) {
return AISL_SYSCALL_ERROR;
}
if (cstuff_list_init(&self->list, 16) != CSTUFF_SUCCESS) {
fclose(f);
return AISL_MALLOC_ERROR;
}
self->fd = f;
return AISL_SUCCESS;
}
void
ax_todo_release(AxTodo self)
{
if (self->fd)
fclose(self->fd);
cstuff_list_release(&self->list, (CStuffListFree)ax_todo_task_free);
}
AislStatus
ax_todo_read(AxTodo self)
{
FILE *f = self->fd;
size_t sz = 256;
char *buf;
ssize_t rs;
AislStatus result = AISL_SUCCESS;
if ((fseek(f, 0L, SEEK_SET) != 0))
return AISL_SYSCALL_ERROR;
if (!(buf = malloc(sz)))
return AISL_MALLOC_ERROR;
while ((rs = getdelim(&buf, &sz, '\n', f)) != -1) {
AxTodoTask task;
if (!(task = ax_todo_task_new()))
goto e_malloc;
if (ax_todo_task_set(task, buf, rs) != 0) {
ax_todo_task_free(task);
goto e_input;
}
if (cstuff_list_append(&self->list, task) == -1) {
ax_todo_task_free(task);
goto e_malloc;
}
}
switch(errno) {
case 0:
goto finally;
case ENOMEM:
goto e_malloc;
default:
goto e_syscall;
}
e_input:
result = AISL_INPUT_ERROR;
goto finally;
e_malloc:
result = AISL_MALLOC_ERROR;
goto finally;
e_syscall:
result = AISL_SYSCALL_ERROR;
finally:
free(buf);
return result;
}
static void
ax_todo__write_dt(FILE *f, time_t dt)
{
char str_dt[16];
struct tm *p_stm = gmtime(&dt);
(void) strftime(str_dt, sizeof (str_dt), "%Y-%m-%d", p_stm);
fprintf(f, "%s ", str_dt);
}
AislStatus
ax_todo_write(AxTodo self)
{
FILE *f = self->fd;
int i;
if ((fseek(f, 0L, SEEK_SET) != 0))
return AISL_SYSCALL_ERROR;
f = self->fd;
for (i = 0; i < self->list.length; i++) {
AxTodoTask task = (AxTodoTask) self->list.items[i];
if (task->is_done) {
fwrite("x ", 1, 2, f);
}
if (task->priority) {
fprintf(f, "(%c) ", task->priority);
}
if (task->is_done) {
if (task->completion) {
ax_todo__write_dt(f, task->completion);
if (task->creation) {
ax_todo__write_dt(f, task->creation);
}
}
} else if (task->creation) {
ax_todo__write_dt(f, task->creation);
}
fprintf(f, "%s\n", task->description);
}
return (fflush(f) == 0) ? AISL_SUCCESS : AISL_SYSCALL_ERROR;
}
AxTodoTask
ax_todo_task_new(void)
{
AxTodoTask result;
if ((result = calloc(1, sizeof (struct ax_todo_task))) != NULL) {
uuid_generate(result->task_id);
}
return result;
}
void
ax_todo_task_release(AxTodoTask task)
{
if (task->description)
free(task->description);
}
void
ax_todo_task_free(AxTodoTask task)
{
ax_todo_task_release(task);
free(task);
}
static int
ax_todo__get_priority(char *buf, char *p_priority)
{
int result;
char priority = 0;
const char fmt[] = {'(', 'A', ')', ' '};
for (result = 0; result < 4; result++) {
switch (result) {
case 0:
case 2:
case 3:
if (buf[result] == fmt[result])
continue;
break;
case 1:
priority = buf[result];
if (!(priority < 0x41 || priority > 0x5A))
continue;
break;
}
return 0;
}
*p_priority = priority;
return result;
}
static int
ax_todo__get_dt(char *buf, time_t *p_dt)
{
struct tm stm = {0};
int result, *p_num = NULL, b_hour = 0, b_min = 0, usec = 0, z_bias = 0;
const char fmt[] = "Y-M-DTH:m:s.uZB:b ", *f = fmt;
char *ptr = NULL;
for (result = 0; *f != 0; result++, f++) {
switch(*f) {
case 'Y':
p_num = &stm.tm_year;
break;
case 'M':
p_num = &stm.tm_mon;
break;
case 'D':
p_num = &stm.tm_mday;
break;
case 'H':
p_num = &stm.tm_hour;
break;
case 'm':
p_num = &stm.tm_min;
break;
case 's':
p_num = &stm.tm_sec;
break;
case 'u':
p_num = &usec;
break;
case 'B':
p_num = &b_hour;
break;
case 'b':
p_num = &b_min;
break;
case '.':
if (buf[result] != *f) {
f++;
}
continue;
case 'T':
if (buf[result] == *f)
continue;
if (buf[result] == ' ') {
f = &fmt[17];
continue;
}
goto e_input;
case 'Z':
if (buf[result] == *f) {
f += 3;
z_bias = 'Z';
} else if (buf[result] == '-') {
z_bias = -1;
} else if (buf[result] == '+') {
z_bias = 1;
} else {
goto e_input;
}
continue;
case '-':
case ':':
case ' ':
if (buf[result] == *f)
continue;
/* go through */
default:
goto e_input;
} /* switch */
*p_num = strtol(&buf[result], &ptr, 10);
if (!ptr || ptr == &buf[result]) {
if (f > &fmt[6] && buf[result] == ' ') {
f = &fmt[17];
/* skip optional part of timestamp */
continue;
}
goto e_input;
}
result += (ptr - &buf[result] - 1);
} /* for */
/* validate results */
/* ToDo: improve (stm.tm_mday < 31) check */
if (!(stm.tm_year < 1900) && (stm.tm_mon) && (stm.tm_mday) &&
(stm.tm_mon < 13) && (stm.tm_mday < 31) && (stm.tm_hour < 24) &&
(stm.tm_min < 60) && (stm.tm_sec < 60)) {
stm.tm_year -= 1900;
stm.tm_mon--;
if (z_bias == 0) { /* local time zone */
*p_dt = mktime(&stm);
return result;
}
if (z_bias != 'Z') {
stm.tm_hour += (b_hour * z_bias);
stm.tm_min += (b_min * z_bias);
}
setenv("TZ", "UTC", 1);
*p_dt = mktime(&stm);
unsetenv("TZ");
return result;
}
e_input:
AX_LOG_DEBUG("Parse error: %c(%d) at %d", *f, (int) (f - fmt), result);
return 0;
(void) usec;
}
AislStatus
ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length)
{
bool is_done;
int l;
if (strncmp(buf, "x ", 2) == 0) {
is_done = true;
buf += 2;
} else {
is_done = false;
}
task->is_done = is_done;
if ((l = ax_todo__get_priority(buf, &task->priority)) != 0) {
buf += l;
}
l = ax_todo__get_dt(buf, (is_done) ? &task->completion : &task->creation);
if (l != 0) {
buf += l;
if (is_done && (l = ax_todo__get_dt(buf, &task->creation)) != 0) {
buf += l;
}
}
if ((l = cstuff_strcpy(&task->description, buf)) == -1) {
return AISL_MALLOC_ERROR;
}
if (l > 1 && task->description[l-1] == '\n')
task->description[l-1] = 0;
return AISL_SUCCESS;
}