tt.c
author llbatlle@comanegra
Fri, 01 Apr 2011 10:06:32 +0200
changeset 21 5dababf1cb7d
parent 20 1567704292af
child 22 3de440cc5319
permissions -rw-r--r--
Milloro l'escript que compta hores.

/*
  tt 1.0 - Time Tracker
    (Instructions using -h)

  Copyright (C) 2010 LluĂ­s Batlle i Rossell
  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
#define _POSIX_C_SOURCE 1
#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <errno.h>

/* For debug messages */
static line_counter;

void parsing_error()
{
    fprintf(stderr, "Parsing error at line %i\n", line_counter);
    exit(1);
}

static FILE *
open_file(const char *attr)
{
    char *name = getenv("TT_PROJECT");
    if (name)
    {
        name = strdup(name);
    }
    else
    {
        const char *home = getenv("HOME");
        const char *filename = "/.tt";
        int len;

        if (!home)
        {
            fprintf(stderr, "You should have $HOME or $TT_PROJECT set.\n");
            exit(1);
        }

        len = strlen(filename) + strlen(home) + 1;
        name = malloc(len);

        snprintf(name, len, "%s%s", home, filename);
    }

    FILE *f;
    f = fopen(name, attr);
    if (!f)
    {
        fprintf(stderr, "Could not open the file: %s (%s)\n", name,
                strerror(errno));
        free(name);
        exit(1);
    }

    free(name);

    return f;
}

static int
get_current_time()
{
    time_t current_time = time(0);
    return (int) current_time;
}

static char *
get_time_str(int d)
{
    time_t seconds = (time_t) d;
    struct tm t;
    localtime_r(&seconds, &t);

    char *str = malloc(100);
    snprintf(str, 100, "%04i-%02i-%02i %02i:%02i:%02i",
             1900+t.tm_year, t.tm_mon+1, t.tm_mday,
             t.tm_hour, t.tm_min, t.tm_sec);
    return str;
}

char *
seconds2hms(int t)
{
    int hours, minutes;

    hours = t / 3600;
    t = t % 3600;
    minutes = t / 60;
    t = t % 60;

    char *str = malloc(100);
    snprintf(str, 100, "%02i:%02i:%02i",
             hours, minutes, t);

    return str;
}

static int
str2time(const char *str)
{
    struct tm t;
    time_t seconds;
    char *str2 = strdup(str);

    char *ptr_begin = str2;
    char *ptr_end;

    ptr_end = strchr(ptr_begin, '-');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &t.tm_year);
    t.tm_year -= 1900;

    ptr_begin = ptr_end + 1;
    ptr_end = strchr(ptr_begin, '-');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &t.tm_mon);
    /* starting at 0 */
    t.tm_mon -= 1;

    ptr_begin = ptr_end + 1;
    ptr_end = strchr(ptr_begin, ' ');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &t.tm_mday);

    ptr_begin = ptr_end + 1;
    ptr_end = strchr(ptr_begin, ':');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &t.tm_hour);

    ptr_begin = ptr_end + 1;
    ptr_end = strchr(ptr_begin, ':');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &t.tm_min);

    ptr_begin = ptr_end + 1;
    sscanf(ptr_begin, "%d", &t.tm_sec);

    free(str2);

    /* We don't know, we had or not DST at that time */
    t.tm_isdst = -1;
    seconds = mktime(&t);
    return (int) seconds;
}

static void
new_task(const char *task)
{
    char *time_str = get_time_str(get_current_time());
    FILE *f = open_file("a");

    fprintf(f, "%s\t%s\n", time_str, task);

    free(time_str);
}

static void
list_task_switches()
{
    FILE *f = open_file("r");

    char buf[1000];
    int n;

    do {
        n = fread(buf, 1, sizeof(buf), f);
        while(n > 0)
        {
            int ret;
            ret = fwrite(buf, 1, n, stdout);
            n -= ret;
            memcpy(buf, buf+ret, n);
        }
    }
    while(!feof(f));
    fclose(f);
}

static void
list_task_times(int show_only_last)
{
    FILE *f = open_file("r");

    int is_prev_time_read = 0;

    int prev_time;
    char *prev_task = 0;
    char *prev_time_str = 0;

    line_counter = 1;

    /* Read lines */
    do {
        /* Read a line */
        char buf[1000];
        int n = 0;
        int c;
        do
        {
            buf[n] = fgetc(f);
            if(buf[n] == '\n' || feof(f))
                break;
            ++n;
        } while(!feof(f) && n <= sizeof(buf));

        buf[n] = 0;
        if(n > 0 && buf[0] != '#')
        {
            char *end_date, *start_task;
            int time;

            end_date = strchr(buf, '\t');
            if (!end_date)
                parsing_error();
            *end_date = 0;
            start_task = end_date + 1;

            time = str2time(buf);
            if (is_prev_time_read && !show_only_last)
            {
                int diff = time - prev_time;
                char *hms = seconds2hms(diff);
                char *prevtimestr2 = get_time_str(prev_time);
                printf("%s %s (%s)\n", hms, prev_task, prevtimestr2);
                free(prevtimestr2);
                free(hms);
            }

            prev_time = time;
            free(prev_task);
            prev_task = strdup(start_task);
            free(prev_time_str);
            prev_time_str = strdup(buf);
            is_prev_time_read = 1;
        }
        else if (n == 0) /* eof */
        {
            /* Time until now, for the last switch */
            int diff = get_current_time() - prev_time;
            char *hms = seconds2hms(diff);
            printf("%s %s (%s)\n", hms, prev_task, prev_time_str);
            free(hms);
            free(prev_task);
            free(prev_time_str);
        }

        line_counter++;
    }
    while(!feof(f));

    fclose(f);
}

char *
str_until_space(const char *str)
{
    while(*str != '\0' && !isspace(*str))
    {
        ++str;
    }

    if (*str == '\0')
        return 0;

    /* We get rid of the const */
    return (char *) str;
}

double
hms2time(const char *str)
{
    int hours, minutes, seconds;
    char *str2 = strdup(str);

    char *ptr_begin = str2;
    char *ptr_end;

    ptr_end = strchr(ptr_begin, ':');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &hours);

    ptr_begin = ptr_end + 1;
    ptr_end = strchr(ptr_begin, ':');
    if (!ptr_end)
        parsing_error();
    *ptr_end = '\0';
    sscanf(ptr_begin, "%d", &minutes);

    ptr_begin = ptr_end + 1;
    sscanf(ptr_begin, "%d", &seconds);

    free(str2);

    return hours * 3600 + minutes * 60 + seconds;
}

static void
sum_times()
{
    int accum_time = 0;
    char *str_hms;

    line_counter = 1;

    /* Read lines */
    do {
        /* Read a line */
        char buf[1000];
        int n = 0;
        int c;
        do
        {
            buf[n] = fgetc(stdin);
            if(buf[n] == '\n' || feof(stdin))
                break;
            ++n;
        } while(!feof(stdin) && n <= sizeof(buf));

        buf[n] = 0;
        if(n > 0)
        {
            char *end;
            int time;

            end = str_until_space(buf);
            if (!end)
                parsing_error();
            *end = 0;

            time = hms2time(buf);
            accum_time += time;
        }
        ++line_counter;
    }
    while(!feof(stdin));

    str_hms = seconds2hms(accum_time);
    printf("%s\n", str_hms);
    free(str_hms);
}

void show_help(const char *prog)
{
    printf(
	    "Usage: %s <-s|-l|-t|-h>\n"
        "       %s <task>\n"
        "Manual:\n"
        "  Switch to a task         :  tt mytask\n"
        "  List task switches       :  tt -l\n"
        "  List time spent per task :  tt -t\n"
        "  Sum times given with -t  :  tt -t | grep ... | tt -s\n"
        "  List what is tracked     :  tt\n"
        "  $TT_PROJECT or ~/.tt stores the tracking.\n"
        , prog, prog);
}

int main(int argc, char **argv)
{
    if (argc >= 2)
        if (strcmp(argv[1], "-t") == 0)
            list_task_times(/* show only last */ 0);
        else if (strcmp(argv[1], "-s") == 0)
            sum_times();
        else if (strcmp(argv[1], "-l") == 0)
            list_task_switches();
        else if (strcmp(argv[1], "-h") == 0)
            show_help(argv[0]);
        else
            new_task(argv[1]);
    else
        list_task_times(/* show only last */ 1);

    return 0;
}