/* function definition to parse a string instead of a file
*/
#include "types.h"
extern BotCommand g_LastCommand = BOT_CMD_NONE;
extern BotExpenseEntry *g_LastExpenseEntry = NULL;
extern BotReminder *g_LastReminder = NULL;
extern GString *g_filename = NULL;
BotCommand bot_get_last_command()
{
return g_LastCommand;
}
void bot_set_last_command(BotCommand cmd)
{
g_LastCommand = cmd;
}
BotExpenseEntry *bot_get_last_expense()
{
return g_LastExpenseEntry;
}
void bot_set_last_expense(BotExpenseEntry *entry)
{
if(g_LastExpenseEntry) {
free_bot_expense_entry(g_LastExpenseEntry);
}
g_LastExpenseEntry = entry;
}
BotReminder *bot_get_last_reminder()
{
return g_LastReminder;
}
void bot_set_last_reminder(BotReminder *reminder)
{
if(g_LastReminder) {
free_bot_reminder(g_LastReminder);
}
g_LastReminder = reminder;
}
GString *bot_get_filename()
{
return g_filename;
}
void bot_set_filename(GString *fn)
{
if(g_filename) {
g_string_free(g_filename, true);
}
g_filename = fn;
}
int bot_get_current_entry_id
(
const GString* filename
)
{
/* TODO this counts by char, probably more efficient ways exist */
char c;
FILE *fp;
unsigned int count = 0;
if((fp = fopen(filename->str, "r"))) {
for (c = getc(fp); c != EOF; c = getc(fp)) {
if (c == '\n') {
count++;
}
}
fclose(fp);
}
return count;
}
/* Create a BotExpenseEntry with data fed from the parser.
* Returns NULL if error getting local time.
*/
BotExpenseEntry *create_bot_expense_entry
(
const BotExpenseEntry *previous,
const unsigned int id,
const BotCommand cmd,
const double value,
GString* user,
GString* notes
)
{
double signedValue = value;
BotExpenseEntry *entry = malloc(sizeof *entry); // TODO this should be freed once it's written to registry
struct tm *lt = malloc(sizeof *lt);
time_t t = time(NULL);
/* get the local time */
localtime_r(&t, lt);
if(lt == NULL) {
fprintf(stderr, "Unable to get localtime.\n");
return NULL;
}
if(cmd == BOT_CMD_OUT) {
signedValue = -value;
}
entry->id = id;
entry->time = *lt;
entry->value = signedValue;
entry->user = user;
entry->notes = notes;
entry->_serialized = false;
return entry;
}
/* FP should not be null. This assumes that it has been initialized with an header line as well
* Returns NULL in case 'entry' is uninitialized.
*/
GString *serialize_bot_expense_entry_csv
(
BotExpenseEntry *entry
)
{
/* will be automatically expanded by g_string_printf */
GString *tstr = g_string_new(NULL);
GString *result = g_string_new(NULL);
if(entry == NULL) {
fprintf(stderr, "BotExpenseEntry 'entry' is NULL. Initialize it before calling 'serialize_bot_expense_entry_csv'\n");
return NULL;
}
/* Time in ISO 8061 */
g_string_printf(tstr,
"%d-%02d-%02d %02d:%02d:%02d",
entry->time.tm_year + 1900,
entry->time.tm_mon + 1,
entry->time.tm_mday,
entry->time.tm_hour,
entry->time.tm_min,
entry->time.tm_sec);
g_string_printf(result,
"%u,%s,%.2f,%s,%s\n",
entry->id,
tstr->str,
entry->value,
entry->user->str,
entry->notes->str
);
entry->_serialized = true;
return result;
}
/* returns the number of bytes written, following the convention of fprintf.
* Returns negative in case of error
*/
int write_bot_expense_entry
(
const GString *filename,
BotExpenseEntry *entry,
bool needsHeaders
)
{
FILE *fp;
int result;
GString *line = serialize_bot_expense_entry_csv(entry);
if(line == NULL) {
return -1;
}
if((fp = fopen(filename->str, "a"))) {
if(needsHeaders) {
fprintf(fp, "ID,Time,Value,User,Notes\n");
}
/* line is terminated with '\n' */
result = fprintf(fp, "%s", line->str);
fclose(fp);
return result;
fprintf(stderr, "Error opening %s\n", filename->str);
}
fprintf(stderr, "Error opening %s\n", filename->str);
return -1;
}
/* whole routine to write an expense to a CSV file */
int write_update_to_csv
(
const GString *filename,
const BotCommand cmd,
const double value,
GString* user,
GString* notes
)
{
int result;
unsigned int id;
BotExpenseEntry *entry;
BotExpenseEntry *previous = bot_get_last_expense();
id = (previous == NULL) ? bot_get_current_entry_id(filename) : previous->id + 1;
entry = create_bot_expense_entry(previous, id, cmd, value, user, notes);
if(entry == NULL) {
return -1;
}
/* if no ID, empty file, initialize with headers before writing entry */
result = write_bot_expense_entry(filename, entry, (id == 0));
if(result > 0) { /* write successful */
bot_set_last_command(cmd);
bot_set_last_expense(entry);
}
return result;
}
/* start a SIGALARM routine */
int start_alarm_reminder(unsigned int minutes)
{
/* in seconds */
alarm(minutes * 60);
BotReminder *reminder = malloc(sizeof *reminder);
reminder->minutes = minutes;
reminder->from = g_string_new(NULL); /* to be set by the message handler of the bot */
bot_set_last_command(BOT_CMD_REMINDER);
bot_set_last_reminder(reminder);
return 0;
}
/* To be called after it has been serialized */
void free_bot_expense_entry
(
BotExpenseEntry *entry
)
{
if(!entry->_serialized) {
/* TODO could fail here instead */
fprintf(stderr, "WARNING: freeing an entry that wasn't serialized!\n");
}
/* Also free underlying string, we don't need it anymore */
g_string_free(entry->user, TRUE);
g_string_free(entry->notes, TRUE);
free(entry);
}
void free_bot_reminder
(
BotReminder *reminder
)
{
g_string_free(reminder->from, true);
free(reminder);
}