/*
* Routines for dealing with tasks
*
* Copyright:
* (C) 1999 Craig Knudsen, cknudsen@cknudsen.com
* See accompanying file "COPYING".
*
* 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., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307, USA
*
* History:
* 09-Mar-2000 Added functions to allow for restoring to
* a previous state: taskMark(), taskMarkAll(),
* taskRestore(), taskRestoreAll()
* 25-Mar-1999 Lots of WIN32 patches made by
* Thomas Epperly <Thomas.Epperly@aspentech.com>
* 18-Mar-1999 Internationalization
* 11-Nov-1998 Added support for options
* 23-Apr-1998 Added new function: taskClearAll
* 22-Mar-1998 Added an extra argument to TaskGetAnnotationEntries
* for time offset.
* 15-Mar-1998 Debugged all malloc/free and found one place
* where free() was not called.
* 15-Mar-1998 Fixed bug that caused SIGSEGV. Only appeared
* if one more tasks (but not the last one) were
* deleted.
* 15-Mar-1998 Added new function: taskAddAnnotation
* 11-Mar-1998 Changed functions to K&R style
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef WIN32
#include <io.h>
#else
#include <dirent.h>
#endif
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#else
#define gettext(a) a
#endif
#include "task.h"
#ifdef GTIMER_MEMDEBUG
#include "memdebug/memdebug.h"
#endif
static int num_tasks = 0;
static Task **tasks = NULL;
static int max_task = -1;
static int last_number = -1;
#ifdef WIN32
static int valid_name ( filename )
char *filename;
{
while ( *filename && isdigit ( *filename ) )
filename++;
return ( ! strcmp ( filename, ".task" ) );
}
#endif
/*
** Add a task.
*/
void taskAdd ( task )
Task *task;
{
int loop;
int new_max_task = 0;
new_max_task = max_task;
if ( task->number == -1 ) {
for ( loop = 0; loop < max_task && task->number < 0; loop++ ) {
if ( tasks[loop] == NULL )
task->number = loop;
}
if ( task->number < 0 )
task->number = ++max_task;
new_max_task = max_task;
} else if ( task->number > max_task ) {
new_max_task = task->number;
}
if ( tasks == NULL ) {
tasks = (Task **) malloc ( sizeof ( Task * ) * ( new_max_task + 1 ) );
for ( loop = 0; loop < new_max_task; loop++ )
tasks[loop] = NULL;
} else
tasks = (Task **) realloc ( tasks,
sizeof ( Task * ) * ( new_max_task + 1 ) );
for ( loop = max_task + 1; loop <= new_max_task; loop++ )
tasks[loop] = NULL;
max_task = new_max_task;
tasks[task->number] = task;
num_tasks++;
}
/*
** Create a new task
*/
Task *taskCreate ( name )
char *name;
{
Task *task;
task = (Task *) malloc ( sizeof ( Task ) );
memset ( task, '\0', sizeof ( Task ) );
task->name = (char *) malloc ( strlen ( name ) + 1 );
strcpy ( task->name, name );
time ( &task->created );
task->number = -1; /* not yet assigned */
return ( task );
}
/*
** Mark the current time in a task. We can then use taskRestore()
** to restore the state of the task to what it was when this function
** was called.
*/
void taskMark ( task )
Task *task;
{
int i;
for ( i = 0; i < task->num_entries; i++ ) {
task->entries[i]->marked_seconds = task->entries[i]->seconds;
}
}
/*
** Mark the current time in all tasks so that we can use taskRestoreAll()
** to restore the state of all tasks to where they are right now.
** NOTE: this is intended to be used only with the timing elements.
** Other stuff like annotations doesn't apply here, but annotations shouldn't
** be added during idle time anyhow...
*/
void taskMarkAll ()
{
int loop;
for ( loop = 0; loop <= max_task; loop++ ) {
if ( tasks[loop] ) {
taskMark ( tasks[loop] );
}
}
}
/*
** Restore a task to its previous state when taskMark() was called.
** This is intended to allow the application to bookmark the state
** of a task and then return to it (as far as timing data goes).
*/
void taskRestore ( task )
Task *task;
{
int i;
for ( i = 0; i < task->num_entries; i++ ) {
task->entries[i]->seconds = task->entries[i]->marked_seconds;
}
}
/*
** Restore all tasks to where they were when taskRestoreAll()
** was called.
*/
void taskRestoreAll ()
{
int loop;
for ( loop = 0; loop <= max_task; loop++ ) {
if ( tasks[loop] ) {
taskRestore ( tasks[loop] );
}
}
}
/*
** Delete a task.
*/
int taskDelete ( task, taskdir )
Task *task;
char *taskdir;
{
char *path;
path = (char *) malloc ( strlen ( taskdir ) + 10 );
sprintf ( path, "%s/%d.task", taskdir, task->number );
unlink ( path );
sprintf ( path, "%s/%d.ann", taskdir, task->number );
unlink ( path );
free ( path );
tasks[task->number] = NULL;
num_tasks--;
taskFree ( task );
return ( 0 );
}
/*
** Return the number of tasks currently loaded.
*/
int taskCount () {
return ( num_tasks );
}
/*
** Get a task by number.
*/
Task *taskGet ( number )
int number;
{
return ( tasks[number] );
}
/*
** Get first task.
*/
Task *taskGetFirst () {
last_number = -1;
return ( taskGetNext() );
}
/*
** Get next task.
*/
Task *taskGetNext () {
int loop;
if ( ! num_tasks )
return ( NULL );
for ( loop = last_number + 1; loop <= max_task; loop++ ) {
if ( tasks[loop] ) {
last_number = loop;
return ( tasks[loop] );
}
}
return ( NULL );
}
/*
** Clear all tasks out of memory.
*/
void taskClearAll ()
{
int loop;
for ( loop = 0; loop <= max_task; loop++ ) {
if ( tasks[loop] ) {
taskFree ( tasks[loop] );
tasks[loop] = 0;
}
}
num_tasks = 0;
last_number = max_task = -1;
}
/*
** Save a task to it's task file.
*/
int taskSave ( task, taskdir )
Task *task;
char *taskdir;
{
char *path;
FILE *fp;
int loop;
path = (char *) malloc ( strlen ( taskdir ) + 10 );
sprintf ( path, "%s/%d.task", taskdir, task->number );
fp = fopen ( path, "w" );
if ( !fp ) {
free ( path );
return ( TASK_ERROR_SYSTEM_ERROR );
}
fprintf ( fp, "Format: 1.2\n" );
fprintf ( fp, "Name: %s\n", task->name );
fprintf ( fp, "Created: %u\n", (unsigned int)task->created );
fprintf ( fp, "Options: %u\n", task->options );
fprintf ( fp, "Project: %d\n", task->project_id );
fprintf ( fp, "Data:\n" );
for ( loop = 0; loop < task->num_entries; loop++ ) {
if ( task->entries[loop]->seconds )
fprintf ( fp, "%04d%02d%02d %d\n",
task->entries[loop]->year, task->entries[loop]->mon,
task->entries[loop]->mday, task->entries[loop]->seconds );
}
fclose ( fp );
free ( path );
return ( 0 );
}
/*
** Save all tasks
*/
int taskSaveAll ( taskdir )
char *taskdir;
{
int loop;
int ret;
for ( loop = 0; loop <= max_task; loop++ ) {
if ( tasks[loop] ) {
ret = taskSave ( tasks[loop], taskdir );
if ( ret )
return ( ret );
}
}
return ( 0 );
}
/*
** Free all resources of a task.
*/
void taskFree ( task )
Task *task;
{
int loop;
free ( task->name );
for ( loop = 0; loop < task->num_entries; loop++ )
free ( task->entries[loop] );
if ( task->entries )
free ( task->entries );
for ( loop = 0; loop < task->num_annotations; loop++ ) {
free ( task->annotations[loop]->text );
free ( task->annotations[loop] );
}
if ( task->annotations )
free ( task->annotations );
free ( task );
}
/*
** Load a task from file.
*/
int taskLoad ( path, task )
char *path;
Task **task;
{
FILE *fp;
int fd;
Task *newtask;
char line[512], *ptr, *ptr2, temp[10], *annfile, *anntext;
int len, created, number, options, project_id = -1;
TaskTimeEntry *entry;
TaskAnnotation *a;
struct stat buf;
fp = fopen ( path, "r" );
if ( !fp )
return ( TASK_ERROR_SYSTEM_ERROR );
for ( ptr = path + strlen ( path ) - 1; *ptr != '/' && ptr != path; ptr-- );
if ( *ptr == '/' )
ptr++;
sscanf ( ptr, "%d.task", &number );
fgets ( line, 512, fp );
len = strlen ( line );
if ( line[len-1] == '\n' )
line[len-1] = '\0';
if ( strcmp ( line, "Format: 1.0" ) && strcmp ( line, "Format: 1.1" ) &&
strcmp ( line, "Format: 1.2" ) ) {
fclose ( fp );
return ( TASK_ERROR_BAD_FILE );
}
newtask = (Task *) malloc ( sizeof ( Task ) );
memset ( newtask, '\0', sizeof ( Task ) );
newtask->number = number;
newtask->project_id = -1; /* no project */
while ( fgets ( line, 512, fp ) ) {
len = strlen ( line );
if ( line[len-1] == '\n' )
line[len-1] = '\0';
if ( strncmp ( line, "Name:", 5 ) == 0 ) {
ptr = line + 5;
if ( *ptr == ' ' )
ptr++;
newtask->name = (char *) malloc ( strlen ( ptr ) + 1 );
strcpy ( newtask->name, ptr );
} else if ( strncmp ( line, "Created:", 8 ) == 0 ) {
sscanf ( line + 8, "%d", &created );
newtask->created = (time_t) created;
} else if ( strncmp ( line, "Project:", 8 ) == 0 ) {
sscanf ( line + 8, "%d", &project_id );
newtask->project_id = project_id;
} else if ( strncmp ( line, "Options:", 8 ) == 0 ) {
sscanf ( line + 8, "%d", &options );
newtask->options = (unsigned int) options;
} else if ( strcmp ( line, "Data:" ) == 0 ) {
while ( fgets ( line, 512, fp ) ) {
entry = (TaskTimeEntry *) malloc ( sizeof ( TaskTimeEntry ) );
memset ( entry, '\0', sizeof ( TaskTimeEntry ) );
strncpy ( temp, line, 4 );
temp[4] = '\0';
entry->year = atoi ( temp );
strncpy ( temp, line + 4, 2 );
temp[2] = '\0';
entry->mon = atoi ( temp );
strncpy ( temp, line + 6, 2 );
temp[2] = '\0';
entry->mday = atoi ( temp );
entry->seconds = atoi ( line + 9 );
entry->marked_seconds = entry->seconds;
if ( ! newtask->entries )
newtask->entries = (TaskTimeEntry **) malloc (
sizeof ( TaskTimeEntry * ) );
else
newtask->entries = (TaskTimeEntry **) realloc ( newtask->entries,
sizeof ( TaskTimeEntry * ) * ( newtask->num_entries + 1 ) );
newtask->entries[newtask->num_entries] = entry;
newtask->num_entries++;
}
break;
} else {
fclose ( fp );
taskFree ( newtask );
return ( TASK_ERROR_BAD_FILE );
}
}
fclose ( fp );
/* now load annotations */
annfile = (char *) malloc ( strlen ( path ) + 1 );
strcpy ( annfile, path );
ptr = annfile + strlen ( annfile ) - 5;
if ( strcmp ( ptr, ".task" ) == 0 ) {
strcpy ( ptr, ".ann" );
if ( stat ( annfile, &buf ) == 0 ) {
fd = open ( annfile, O_RDONLY );
anntext = (char *) malloc ( buf.st_size + 1 );
read ( fd, anntext, buf.st_size );
anntext[buf.st_size] = '\0';
close ( fd );
ptr = strtok ( anntext, "\n" );
while ( ptr ) {
ptr2 = ptr;
while ( isdigit ( *ptr2 ) )
ptr2++;
if ( *ptr2 == ' ' ) {
*ptr2 = '\0';
a = (TaskAnnotation *) malloc ( sizeof ( TaskAnnotation ) );
memset ( a, '\0', sizeof ( TaskAnnotation ) );
a->text_time = atoi ( ptr );
ptr2++;
a->text = (char *) malloc ( strlen ( ptr2 ) + 1 );
strcpy ( a->text, ptr2 );
for ( ptr2 = a->text; *ptr2 != '\0'; ptr2++ )
if ( *ptr2 == '\r' )
*ptr2 = '\n';
if ( newtask->annotations == NULL ) {
newtask->annotations = (TaskAnnotation **) malloc (
sizeof ( TaskAnnotation * ) );
} else {
newtask->annotations = (TaskAnnotation **) realloc (
newtask->annotations,
( newtask->num_annotations + 1 ) * sizeof ( TaskAnnotation * ) );
}
newtask->annotations[newtask->num_annotations] = a;
newtask->num_annotations++;
}
ptr = strtok ( NULL, "\n" );
}
free ( anntext );
}
}
free ( annfile );
taskAdd ( newtask );
*task = newtask;
return ( 0 );
}
int taskLoadAll ( taskdir )
char *taskdir;
{
#ifdef WIN32
char *pattern;
Task *task;
long handle;
struct _finddata_t fdata;
pattern = malloc ( strlen ( taskdir ) + 8 );
(void) strcat ( strcpy ( pattern, taskdir ), "/*.task" );
if ( ( handle = _findfirst ( pattern, &fdata ) ) != -1 ) {
char *path = malloc ( strlen ( taskdir ) + _MAX_FNAME + 2 );
char *start = path + strlen ( taskdir ) + 1;
(void) strcat ( strcpy ( path, taskdir ), "/" );
do {
(void) strcpy ( start, fdata.name );
if ( valid_name ( fdata.name ) && !_access ( path, 4 ) ){
taskLoad ( path, &task );
}
} while ( !_findnext ( handle, &fdata ) );
_findclose ( handle );
free ( path );
}
else {
free ( pattern );
return ( TASK_ERROR_SYSTEM_ERROR );
}
free ( pattern );
#else
DIR *dir;
struct dirent *entry;
struct stat buf;
char *path, *ptr;
Task *task;
dir = opendir ( taskdir );
if ( ! dir )
return ( TASK_ERROR_SYSTEM_ERROR );
while ( ( entry = readdir ( dir ) ) ) {
path = (char *) malloc ( strlen ( taskdir ) + strlen ( entry->d_name )
+ 2 );
sprintf ( path, "%s/%s", taskdir, entry->d_name );
if ( stat ( path, &buf ) == 0 ) {
for ( ptr = entry->d_name; isdigit ( *ptr ); ptr++ ) ;
if ( strcmp ( ptr, ".task" ) == 0 && S_ISREG ( buf.st_mode ) ) {
/* NOTE: add catching of errors here... */
taskLoad ( path, &task );
}
}
free ( path );
}
#endif
return ( 0 );
}
char *taskErrorString ( task_error )
int task_error;
{
static char msg[256];
switch ( task_error ) {
case TASK_ERROR_SYSTEM_ERROR:
sprintf ( msg, "%s (%d)", gettext("System Error"), errno );
return ( msg );
case TASK_ERROR_BAD_FILE:
return ( gettext("Invalid task data file format") );
default:
sprintf ( msg, "%s(%d)", gettext("Unknown error"),
task_error );
return ( msg );
}
}
TaskTimeEntry *taskGetTimeEntry ( task, year, month, day )
Task *task;
int year, month, day;
{
int loop;
struct tm *tm;
time_t now;
if ( year < 100 ) {
time ( &now );
tm = localtime ( &now );
year += 1900 + ( tm->tm_year % 100 );
}
for ( loop = 0; loop < task->num_entries; loop++ ) {
if ( task->entries[loop]->year == year &&
task->entries[loop]->mon == month &&
task->entries[loop]->mday == day )
return ( task->entries[loop] );
}
return ( NULL );
}
TaskTimeEntry *taskNewTimeEntry ( task, year, month, day )
Task *task;
int year, month, day;
{
struct tm *tm;
time_t now;
TaskTimeEntry *ret;
if ( year < 100 ) {
time ( &now );
tm = localtime ( &now );
year += 1900 + ( tm->tm_year % 100 );
}
ret = (TaskTimeEntry *) malloc ( sizeof ( TaskTimeEntry ) );
ret->year = year;
ret->mon = month;
ret->mday = day;
ret->seconds = 0;
ret->marked_seconds = 0;
if ( task->entries )
task->entries = (TaskTimeEntry **) realloc ( task->entries,
( task->num_entries + 1 ) * ( sizeof ( TaskTimeEntry * ) ) );
else
task->entries = (TaskTimeEntry **) malloc ( ( task->num_entries + 1 )
* ( sizeof ( TaskTimeEntry * ) ) );
task->entries[task->num_entries] = ret;
task->num_entries++;
return ( ret );
}
/*
** Get the options for the specified task.
*/
unsigned int taskGetOptions ( task )
Task *task;
{
return task->options;
}
/*
** Determine if the specified option is enabled.
*/
unsigned int taskOptionEnabled ( task, option )
Task *task;
unsigned int option;
{
if ( option & task->options )
return 1;
else
return 0;
}
void taskSetOption ( task, option )
Task *task;
unsigned int option;
{
task->options |= option;
}
void taskUnsetOption ( task, option )
Task *task;
unsigned int option;
{
if ( taskOptionEnabled ( task, option ) )
task->options -= option;
}
/*
** Add a task annotation to a task. Can be more than one line of
** text. We will convert '\n' into '\r' before saving and then
** back when loading.
** Note: It gets saved immediately (not when taskSave is called)
*/
void taskAddAnnotation ( task, taskdir, text )
Task *task;
char *taskdir;
char *text;
{
char *ptr, *path, *newtext;
TaskAnnotation *a;
FILE *fp;
a = (TaskAnnotation *) malloc ( sizeof ( TaskAnnotation ) );
memset ( a, '\0', sizeof ( TaskAnnotation ) );
time ( &a->text_time );
a->text = (char *) malloc ( strlen ( text ) + 1 );
strcpy ( a->text, text );
if ( task->annotations == NULL ) {
task->annotations = (TaskAnnotation **) malloc (
sizeof ( TaskAnnotation * ) );
} else {
task->annotations = (TaskAnnotation **) realloc ( task->annotations,
( task->num_annotations + 1 ) * sizeof ( TaskAnnotation * ) );
}
task->annotations[task->num_annotations] = a;
task->num_annotations++;
/* now save to file */
path = (char *) malloc ( strlen ( taskdir ) + 10 );
sprintf ( path, "%s/%d.ann", taskdir, task->number );
fp = fopen ( path, "a+" );
if ( fp ) {
newtext = (char *) malloc ( strlen ( text ) + 1 );
strcpy ( newtext, text );
for ( ptr = newtext; *ptr != '\0'; ptr++ )
if ( *ptr == '\n' )
*ptr = '\r';
fprintf ( fp, "%d %s\n", (int)a->text_time, newtext );
free ( newtext );
fclose ( fp );
}
free ( path );
}
/*
** Get all annotations for the specified task on the specified day.
** NOTE: Caller must free return value.
** ??? Maybe we should take the midnight offset into consideration here ???
*/
TaskAnnotation **TaskGetAnnotationEntries ( task, year, month, day,
time_offset, num_ret )
Task *task;
int year, month, day;
int time_offset;
int *num_ret;
{
TaskAnnotation **ret = NULL;
int loop = 0;
struct tm *tm;
int num = 0;
time_t then;
for ( loop = 0; loop < task->num_annotations; loop++ ) {
then = task->annotations[loop]->text_time - time_offset;
tm = localtime ( &then );
if ( tm->tm_year + 1900 == year &&
tm->tm_mon + 1 == month &&
tm->tm_mday == day ) {
if ( num ) {
ret = (TaskAnnotation **) realloc ( ret,
( num + 1 ) * sizeof ( TaskAnnotation * ) );
} else {
ret = (TaskAnnotation **) malloc ( sizeof ( TaskAnnotation * ) );
}
ret[num] = task->annotations[loop];
num++;
}
}
*num_ret = num;
return ( ret );
}