1 /*
2  * Routines for dealing with tasks
3  *
4  * Copyright:
5  *	(C) 1999 Craig Knudsen, cknudsen@cknudsen.com
6  *	See accompanying file "COPYING".
7  *
8  *	This program is free software; you can redistribute it and/or
9  *	modify it under the terms of the GNU General Public License
10  *	as published by the Free Software Foundation; either version 2
11  *	of the License, or (at your option) any later version.
12  *
13  *	This program is distributed in the hope that it will be useful,
14  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *	GNU General Public License for more details.
17  *
18  *	You should have received a copy of the GNU General Public License
19  *	along with this program; if not, write to the
20  *	Free Software Foundation, Inc., 59 Temple Place,
21  *	Suite 330, Boston, MA  02111-1307, USA
22  *
23  * History:
24  *	17-Apr-2005	Add support for subtracting a particular offset
25  *			off of timers.  (Russ Allbery)
26  *	09-Mar-2000	Added functions to allow for restoring to
27  *			a previous state: taskMark(), taskMarkAll(),
28  *			taskRestore(), taskRestoreAll()
29  *	25-Mar-1999	Lots of WIN32 patches made by
30  *			Thomas Epperly <Thomas.Epperly@aspentech.com>
31  *	18-Mar-1999	Internationalization
32  *	11-Nov-1998	Added support for options
33  *	23-Apr-1998	Added new function: taskClearAll
34  *	22-Mar-1998	Added an extra argument to TaskGetAnnotationEntries
35  *			for time offset.
36  *	15-Mar-1998	Debugged all malloc/free and found one place
37  *			where free() was not called.
38  *	15-Mar-1998	Fixed bug that caused SIGSEGV.  Only appeared
39  *			if one more tasks (but not the last one) were
40  *			deleted.
41  *	15-Mar-1998	Added new function: taskAddAnnotation
42  *	11-Mar-1998	Changed functions to K&R style
43  *
44  */
45 
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <time.h>
50 #if HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #ifdef WIN32
54 #include <io.h>
55 #else
56 #include <dirent.h>
57 #endif
58 #include <errno.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <ctype.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <fcntl.h>
65 
66 #include "task.h"
67 
68 #ifdef GTIMER_MEMDEBUG
69 #include "memdebug/memdebug.h"
70 #endif
71 
72 // PV: Internationalization
73 #include "gtimeri18n.h"
74 
75 static int num_tasks = 0;
76 static Task **tasks = NULL;
77 static int max_task = -1;
78 static int last_number = -1;
79 
80 
81 #ifdef WIN32
valid_name(filename)82 static int valid_name ( filename )
83 char *filename;
84 {
85   while ( *filename && isdigit ( *filename ) )
86     filename++;
87   return ( ! strcmp ( filename, ".task" ) );
88 }
89 #endif
90 
91 
92 /*
93 ** Add a task.
94 */
taskAdd(task)95 void taskAdd ( task )
96 Task *task;
97 {
98   int loop;
99   int new_max_task = 0;
100 
101   new_max_task = max_task;
102 
103   if ( task->number == -1 ) {
104     for ( loop = 0; loop < max_task && task->number < 0; loop++ ) {
105       if ( tasks[loop] == NULL )
106         task->number = loop;
107     }
108     if ( task->number < 0 )
109       task->number = ++max_task;
110     new_max_task = max_task;
111   } else if ( task->number > max_task ) {
112     new_max_task = task->number;
113   }
114 
115   if ( tasks == NULL ) {
116     tasks = (Task **) malloc ( sizeof ( Task * ) * ( new_max_task + 1 ) );
117     for ( loop = 0; loop < new_max_task; loop++ )
118       tasks[loop] = NULL;
119   } else
120     tasks = (Task **) realloc ( tasks,
121       sizeof ( Task * ) * ( new_max_task + 1 ) );
122 
123   for ( loop = max_task + 1; loop <= new_max_task; loop++ )
124     tasks[loop] = NULL;
125 
126   max_task = new_max_task;
127 
128   tasks[task->number] = task;
129   num_tasks++;
130 }
131 
132 
133 
134 /*
135 ** Create a new task
136 */
taskCreate(name)137 Task *taskCreate ( name )
138 char *name;
139 {
140   Task *task;
141 
142   task = (Task *) malloc ( sizeof ( Task ) );
143   memset ( task, '\0', sizeof ( Task ) );
144   task->name = (char *) malloc ( strlen ( name ) + 1 );
145   strcpy ( task->name, name );
146   time ( &task->created );
147   task->number = -1; /* not yet assigned */
148 
149   return ( task );
150 }
151 
152 
153 /*
154 ** Mark the current time in a task.  We can then use taskRestore()
155 ** to restore the state of the task to what it was when this function
156 ** was called.
157 */
taskMark(task,offset)158 void taskMark ( task, offset )
159 Task *task;
160 int offset;
161 {
162   int i;
163   int seconds;
164 
165   for ( i = 0; i < task->num_entries; i++ ) {
166     task->entries[i]->marked_seconds = task->entries[i]->seconds;
167   }
168   if ( offset > 0 && task->num_entries > 0 ) {
169     seconds = task->entries[task->num_entries - 1]->marked_seconds;
170     seconds -= offset;
171     if ( seconds < 0 )
172       seconds = 0;
173     task->entries[task->num_entries - 1]->marked_seconds = seconds;
174   }
175 }
176 
177 
178 /*
179 ** Mark the current time in all tasks so that we can use taskRestoreAll()
180 ** to restore the state of all tasks to where they are right now.
181 ** NOTE: this is intended to be used only with the timing elements.
182 ** Other stuff like annotations doesn't apply here, but annotations shouldn't
183 ** be added during idle time anyhow...
184 */
taskMarkAll(offset)185 void taskMarkAll ( offset )
186 int offset;
187 {
188   int loop;
189 
190   for ( loop = 0; loop <= max_task; loop++ ) {
191     if ( tasks[loop] ) {
192       taskMark ( tasks[loop], offset );
193     }
194   }
195 }
196 
197 
198 /*
199 ** Restore a task to its previous state when taskMark() was called.
200 ** This is intended to allow the application to bookmark the state
201 ** of a task and then return to it (as far as timing data goes).
202 */
taskRestore(task)203 void taskRestore ( task )
204 Task *task;
205 {
206   int i;
207 
208   for ( i = 0; i < task->num_entries; i++ ) {
209     task->entries[i]->seconds = task->entries[i]->marked_seconds;
210   }
211 }
212 
213 
214 
215 /*
216 ** Restore all tasks to where they were when taskRestoreAll()
217 ** was called.
218 */
taskRestoreAll()219 void taskRestoreAll ()
220 {
221   int loop;
222 
223   for ( loop = 0; loop <= max_task; loop++ ) {
224     if ( tasks[loop] ) {
225       taskRestore ( tasks[loop] );
226     }
227   }
228 }
229 
230 
231 
232 /*
233 ** Delete a task.
234 */
taskDelete(task,taskdir)235 int taskDelete ( task, taskdir )
236 Task *task;
237 char *taskdir;
238 {
239   char *path;
240 
241   path = (char *) malloc ( strlen ( taskdir ) + 10 );
242   sprintf ( path, "%s/%d.task", taskdir, task->number );
243   unlink ( path );
244   sprintf ( path, "%s/%d.ann", taskdir, task->number );
245   unlink ( path );
246   free ( path );
247 
248   tasks[task->number] = NULL;
249   num_tasks--;
250   taskFree ( task );
251 
252   return ( 0 );
253 }
254 
255 
256 
257 /*
258 ** Return the number of tasks currently loaded.
259 */
taskCount()260 int taskCount () {
261   return ( num_tasks );
262 }
263 
264 
265 
266 /*
267 ** Get a task by number.
268 */
taskGet(number)269 Task *taskGet ( number )
270 int number;
271 {
272   return ( tasks[number] );
273 }
274 
275 
276 /*
277 ** Get first task.
278 */
taskGetFirst()279 Task *taskGetFirst () {
280   last_number = -1;
281   return ( taskGetNext() );
282 }
283 
284 
285 /*
286 ** Get next task.
287 */
taskGetNext()288 Task *taskGetNext () {
289   int loop;
290 
291   if ( ! num_tasks )
292     return ( NULL );
293 
294   for ( loop = last_number + 1; loop <= max_task; loop++ ) {
295     if ( tasks[loop] ) {
296       last_number = loop;
297       return ( tasks[loop] );
298     }
299   }
300 
301   return ( NULL );
302 }
303 
304 
305 
306 /*
307 ** Clear all tasks out of memory.
308 */
taskClearAll()309 void taskClearAll ()
310 {
311   int loop;
312 
313   for ( loop = 0; loop <= max_task; loop++ ) {
314     if ( tasks[loop] ) {
315       taskFree ( tasks[loop] );
316       tasks[loop] = 0;
317     }
318   }
319   num_tasks = 0;
320   last_number = max_task = -1;
321 }
322 
323 
324 /*
325 ** Save a task to it's task file.
326 */
taskSave(task,taskdir)327 int taskSave ( task, taskdir )
328 Task *task;
329 char *taskdir;
330 {
331   char *path;
332   FILE *fp;
333   int loop;
334 
335   path = (char *) malloc ( strlen ( taskdir ) + 10 );
336   sprintf ( path, "%s/%d.task", taskdir, task->number );
337 
338   fp = fopen ( path, "w" );
339   if ( !fp ) {
340     free ( path );
341     return ( TASK_ERROR_SYSTEM_ERROR );
342   }
343 
344   fprintf ( fp, "Format: 1.2\n" );
345   fprintf ( fp, "Name: %s\n", task->name );
346   fprintf ( fp, "Created: %u\n", (unsigned int)task->created );
347   fprintf ( fp, "Options: %u\n", task->options );
348   fprintf ( fp, "Project: %d\n", task->project_id );
349   fprintf ( fp, "Data:\n" );
350 
351   for ( loop = 0; loop < task->num_entries; loop++ ) {
352     if ( task->entries[loop]->seconds )
353       fprintf ( fp, "%04d%02d%02d %d\n",
354         task->entries[loop]->year, task->entries[loop]->mon,
355         task->entries[loop]->mday, task->entries[loop]->seconds );
356   }
357 
358   fclose ( fp );
359   free ( path );
360 
361   return ( 0 );
362 }
363 
364 
365 /*
366 ** Save all tasks
367 */
taskSaveAll(taskdir)368 int taskSaveAll ( taskdir )
369 char *taskdir;
370 {
371   int loop;
372   int ret;
373 
374   for ( loop = 0; loop <= max_task; loop++ ) {
375     if ( tasks[loop] ) {
376       ret = taskSave ( tasks[loop], taskdir );
377       if ( ret )
378         return ( ret );
379     }
380   }
381   return ( 0 );
382 }
383 
384 
385 
386 /*
387 ** Free all resources of a task.
388 */
taskFree(task)389 void taskFree ( task )
390 Task *task;
391 {
392   int loop;
393   free ( task->name );
394   for ( loop = 0; loop < task->num_entries; loop++ )
395     free ( task->entries[loop] );
396   if ( task->entries )
397     free ( task->entries );
398   for ( loop = 0; loop < task->num_annotations; loop++ ) {
399     free ( task->annotations[loop]->text );
400     free ( task->annotations[loop] );
401   }
402   if ( task->annotations )
403     free ( task->annotations );
404   free ( task );
405 }
406 
407 
408 
409 /*
410 ** Load a task from file.
411 */
taskLoad(path,task)412 int taskLoad ( path, task )
413 char *path;
414 Task **task;
415 {
416   FILE *fp;
417   int fd;
418   Task *newtask;
419   char line[512], *ptr, *ptr2, temp[10], *annfile, *anntext;
420   int len, created, number, options, project_id = -1;
421   TaskTimeEntry *entry;
422   TaskAnnotation *a;
423   struct stat buf;
424 
425   fp = fopen ( path, "r" );
426   if ( !fp )
427     return ( TASK_ERROR_SYSTEM_ERROR );
428 
429   for ( ptr = path + strlen ( path ) - 1; *ptr != '/' && ptr != path; ptr-- );
430   if ( *ptr == '/' )
431     ptr++;
432   sscanf ( ptr, "%d.task", &number );
433 
434   fgets ( line, 512, fp );
435   len = strlen ( line );
436   if ( line[len-1] == '\n' )
437     line[len-1] = '\0';
438   if ( strcmp ( line, "Format: 1.0" ) && strcmp ( line, "Format: 1.1" ) &&
439     strcmp ( line, "Format: 1.2" ) ) {
440     fclose ( fp );
441     return ( TASK_ERROR_BAD_FILE );
442   }
443 
444   newtask = (Task *) malloc ( sizeof ( Task ) );
445   memset ( newtask, '\0', sizeof ( Task ) );
446   newtask->number = number;
447   newtask->project_id = -1; /* no project */
448 
449   while ( fgets ( line, 512, fp ) ) {
450     len = strlen ( line );
451     if ( line[len-1] == '\n' )
452       line[len-1] = '\0';
453     if ( strncmp ( line, "Name:", 5 ) == 0 ) {
454       ptr = line + 5;
455       if ( *ptr == ' ' )
456         ptr++;
457       newtask->name = (char *) malloc ( strlen ( ptr ) + 1 );
458       strcpy ( newtask->name, ptr );
459     } else if ( strncmp ( line, "Created:", 8 ) == 0 ) {
460       sscanf ( line + 8, "%d", &created );
461       newtask->created = (time_t) created;
462     } else if ( strncmp ( line, "Project:", 8 ) == 0 ) {
463       sscanf ( line + 8, "%d", &project_id );
464       newtask->project_id = project_id;
465     } else if ( strncmp ( line, "Options:", 8 ) == 0 ) {
466       sscanf ( line + 8, "%d", &options );
467       newtask->options = (unsigned int) options;
468     } else if ( strcmp ( line, "Data:" ) == 0 ) {
469       while ( fgets ( line, 512, fp ) ) {
470         entry = (TaskTimeEntry *) malloc ( sizeof ( TaskTimeEntry ) );
471         memset ( entry, '\0', sizeof ( TaskTimeEntry ) );
472         strncpy ( temp, line, 4 );
473         temp[4] = '\0';
474         entry->year = atoi ( temp );
475         strncpy ( temp, line + 4, 2 );
476         temp[2] = '\0';
477         entry->mon = atoi ( temp );
478         strncpy ( temp, line + 6, 2 );
479         temp[2] = '\0';
480         entry->mday = atoi ( temp );
481         entry->seconds = atoi ( line + 9 );
482         entry->marked_seconds = entry->seconds;
483         if ( ! newtask->entries )
484           newtask->entries = (TaskTimeEntry **) malloc (
485             sizeof ( TaskTimeEntry * ) );
486         else
487           newtask->entries = (TaskTimeEntry **) realloc ( newtask->entries,
488             sizeof ( TaskTimeEntry * ) * ( newtask->num_entries + 1 ) );
489         newtask->entries[newtask->num_entries] = entry;
490         newtask->num_entries++;
491       }
492       break;
493     } else {
494       fclose ( fp );
495       taskFree ( newtask );
496       return ( TASK_ERROR_BAD_FILE );
497     }
498   }
499   fclose ( fp );
500 
501   /* now load annotations */
502   annfile = (char *) malloc ( strlen ( path ) + 1 );
503   strcpy ( annfile, path );
504   ptr = annfile + strlen ( annfile ) - 5;
505   if ( strcmp ( ptr, ".task" ) == 0 ) {
506     strcpy ( ptr, ".ann" );
507     if ( stat ( annfile, &buf ) == 0 ) {
508       fd = open ( annfile, O_RDONLY );
509       anntext = (char *) malloc ( buf.st_size + 1 );
510       read ( fd, anntext, buf.st_size );
511       anntext[buf.st_size] = '\0';
512       close ( fd );
513       ptr = strtok ( anntext, "\n" );
514       while ( ptr ) {
515         ptr2 = ptr;
516         while ( isdigit ( *ptr2 ) )
517           ptr2++;
518         if ( *ptr2 == ' ' ) {
519           *ptr2 = '\0';
520           a = (TaskAnnotation *) malloc ( sizeof ( TaskAnnotation ) );
521           memset ( a, '\0', sizeof ( TaskAnnotation ) );
522           a->text_time = atoi ( ptr );
523           ptr2++;
524           a->text = (char *) malloc ( strlen ( ptr2 ) + 1 );
525           strcpy ( a->text, ptr2 );
526           for ( ptr2 = a->text; *ptr2 != '\0'; ptr2++ )
527             if ( *ptr2 == '\r' )
528               *ptr2 = '\n';
529           if ( newtask->annotations == NULL ) {
530             newtask->annotations = (TaskAnnotation **) malloc (
531               sizeof ( TaskAnnotation * ) );
532           } else {
533             newtask->annotations = (TaskAnnotation **) realloc (
534               newtask->annotations,
535               ( newtask->num_annotations + 1 ) * sizeof ( TaskAnnotation * ) );
536           }
537           newtask->annotations[newtask->num_annotations] = a;
538           newtask->num_annotations++;
539         }
540         ptr = strtok ( NULL, "\n" );
541       }
542       free ( anntext );
543     }
544   }
545   free ( annfile );
546 
547   taskAdd ( newtask );
548 
549   *task = newtask;
550 
551   return ( 0 );
552 }
553 
554 
555 
taskLoadAll(taskdir)556 int taskLoadAll ( taskdir )
557 char *taskdir;
558 {
559 #ifdef WIN32
560    char *pattern;
561    Task *task;
562    long handle;
563    struct _finddata_t fdata;
564    pattern = malloc ( strlen ( taskdir ) + 8 );
565    (void) strcat ( strcpy ( pattern, taskdir ), "/*.task" );
566    if ( ( handle = _findfirst ( pattern, &fdata ) ) != -1 ) {
567      char *path = malloc ( strlen ( taskdir ) + _MAX_FNAME + 2 );
568      char *start = path + strlen ( taskdir ) + 1;
569      (void) strcat ( strcpy ( path, taskdir ), "/" );
570      do {
571        (void) strcpy ( start, fdata.name );
572        if ( valid_name ( fdata.name ) && !_access ( path, 4 ) ){
573          taskLoad ( path, &task );
574        }
575      } while ( !_findnext ( handle, &fdata ) );
576      _findclose ( handle );
577      free ( path );
578    }
579    else {
580      free ( pattern );
581      return ( TASK_ERROR_SYSTEM_ERROR );
582    }
583    free ( pattern );
584 #else
585   DIR *dir;
586   struct dirent *entry;
587   struct stat buf;
588   char *path, *ptr;
589   Task *task;
590 
591   dir = opendir ( taskdir );
592   if ( ! dir )
593     return ( TASK_ERROR_SYSTEM_ERROR );
594   while ( ( entry = readdir ( dir ) ) ) {
595     path = (char *) malloc ( strlen ( taskdir ) + strlen ( entry->d_name )
596       + 2 );
597     sprintf ( path, "%s/%s", taskdir, entry->d_name );
598     if ( stat ( path, &buf ) == 0 ) {
599       for ( ptr = entry->d_name; isdigit ( *ptr ); ptr++ ) ;
600       if ( strcmp ( ptr, ".task" ) == 0 && S_ISREG ( buf.st_mode ) ) {
601         /* NOTE: add catching of errors here... */
602         taskLoad ( path, &task );
603       }
604     }
605     free ( path );
606   }
607 
608 #endif
609   return ( 0 );
610 }
611 
612 
taskErrorString(task_error)613 char *taskErrorString ( task_error )
614 int task_error;
615 {
616   static char msg[256];
617 
618   switch ( task_error ) {
619     case TASK_ERROR_SYSTEM_ERROR:
620       sprintf ( msg, gettext("System Error (%d)"), errno );
621       return ( msg );
622     case TASK_ERROR_BAD_FILE:
623       return ( gettext("Invalid task data file format") );
624     default:
625       sprintf ( msg,  gettext("Unknown error (%d)"), task_error );
626       return ( msg );
627   }
628 }
629 
630 
631 
632 
taskGetTimeEntry(task,year,month,day)633 TaskTimeEntry *taskGetTimeEntry ( task, year, month, day )
634 Task *task;
635 int year, month, day;
636 {
637   int loop;
638   struct tm *tm;
639   time_t now;
640 
641   if ( year < 100 ) {
642     time ( &now );
643     tm = localtime ( &now );
644     year += 1900 + ( tm->tm_year % 100 );
645   }
646 
647   for ( loop = 0; loop < task->num_entries; loop++ ) {
648     if ( task->entries[loop]->year == year &&
649       task->entries[loop]->mon == month &&
650       task->entries[loop]->mday == day )
651       return ( task->entries[loop] );
652   }
653 
654   return ( NULL );
655 }
656 
657 
658 
659 
taskNewTimeEntry(task,year,month,day)660 TaskTimeEntry *taskNewTimeEntry ( task, year, month, day )
661 Task *task;
662 int year, month, day;
663 {
664   struct tm *tm;
665   time_t now;
666   TaskTimeEntry *ret;
667 
668   if ( year < 100 ) {
669     time ( &now );
670     tm = localtime ( &now );
671     year += 1900 + ( tm->tm_year % 100 );
672   }
673 
674   ret = (TaskTimeEntry *) malloc ( sizeof ( TaskTimeEntry ) );
675 
676   ret->year = year;
677   ret->mon = month;
678   ret->mday = day;
679   ret->seconds = 0;
680   ret->marked_seconds = 0;
681 
682   if ( task->entries )
683     task->entries = (TaskTimeEntry **) realloc ( task->entries,
684       ( task->num_entries + 1 ) * ( sizeof ( TaskTimeEntry * ) ) );
685   else
686     task->entries = (TaskTimeEntry **) malloc ( ( task->num_entries + 1 )
687       * ( sizeof ( TaskTimeEntry * ) ) );
688   task->entries[task->num_entries] = ret;
689   task->num_entries++;
690 
691   return ( ret );
692 }
693 
694 
695 /*
696 ** Get the options for the specified task.
697 */
taskGetOptions(task)698 unsigned int taskGetOptions ( task )
699 Task *task;
700 {
701   return task->options;
702 }
703 
704 
705 /*
706 ** Determine if the specified option is enabled.
707 */
taskOptionEnabled(task,option)708 unsigned int taskOptionEnabled ( task, option )
709 Task *task;
710 unsigned int option;
711 {
712   if ( option & task->options )
713     return 1;
714   else
715     return 0;
716 }
717 
718 
taskSetOption(task,option)719 void taskSetOption ( task, option )
720 Task *task;
721 unsigned int option;
722 {
723   task->options |= option;
724 }
725 
taskUnsetOption(task,option)726 void taskUnsetOption ( task, option )
727 Task *task;
728 unsigned int option;
729 {
730   if ( taskOptionEnabled ( task, option ) )
731     task->options -= option;
732 }
733 
734 /*
735 ** Add a task annotation to a task.  Can be more than one line of
736 ** text.  We will convert '\n' into '\r' before saving and then
737 ** back when loading.
738 ** Note: It gets saved immediately (not when taskSave is called)
739 */
taskAddAnnotation(task,taskdir,text)740 void taskAddAnnotation ( task, taskdir, text )
741 Task *task;
742 char *taskdir;
743 char *text;
744 {
745   char *ptr, *path, *newtext;
746   TaskAnnotation *a;
747   FILE *fp;
748 
749   a = (TaskAnnotation *) malloc ( sizeof ( TaskAnnotation ) );
750   memset ( a, '\0', sizeof ( TaskAnnotation ) );
751   time ( &a->text_time );
752   a->text = (char *) malloc ( strlen ( text ) + 1 );
753   strcpy ( a->text, text );
754 
755   if ( task->annotations == NULL ) {
756     task->annotations = (TaskAnnotation **) malloc (
757       sizeof ( TaskAnnotation * ) );
758   } else {
759     task->annotations = (TaskAnnotation **) realloc ( task->annotations,
760       ( task->num_annotations + 1 ) * sizeof ( TaskAnnotation * ) );
761   }
762   task->annotations[task->num_annotations] = a;
763   task->num_annotations++;
764 
765   /* now save to file */
766   path = (char *) malloc ( strlen ( taskdir ) + 10 );
767   sprintf ( path, "%s/%d.ann", taskdir, task->number );
768   fp = fopen ( path, "a+" );
769   if ( fp ) {
770     newtext = (char *) malloc ( strlen ( text ) + 1 );
771     strcpy ( newtext, text );
772     for ( ptr = newtext; *ptr != '\0'; ptr++ )
773       if ( *ptr == '\n' )
774         *ptr = '\r';
775     fprintf ( fp, "%d %s\n", (int)a->text_time, newtext );
776     free ( newtext );
777     fclose ( fp );
778   }
779   free ( path );
780 }
781 
782 
783 
784 /*
785 ** Get all annotations for the specified task on the specified day.
786 ** NOTE: Caller must free return value.
787 ** ??? Maybe we should take the midnight offset into consideration here ???
788 */
TaskGetAnnotationEntries(task,year,month,day,time_offset,num_ret)789 TaskAnnotation **TaskGetAnnotationEntries ( task, year, month, day,
790   time_offset, num_ret )
791 Task *task;
792 int year, month, day;
793 int time_offset;
794 int *num_ret;
795 {
796   TaskAnnotation **ret = NULL;
797   int loop = 0;
798   struct tm *tm;
799   int num = 0;
800   time_t then;
801 
802   for ( loop = 0; loop < task->num_annotations; loop++ ) {
803     then = task->annotations[loop]->text_time - time_offset;
804     tm = localtime ( &then );
805     if ( tm->tm_year + 1900 == year &&
806       tm->tm_mon + 1 == month &&
807       tm->tm_mday == day ) {
808       if ( num ) {
809         ret = (TaskAnnotation **) realloc ( ret,
810           ( num + 1 ) * sizeof ( TaskAnnotation * ) );
811       } else {
812         ret = (TaskAnnotation **) malloc ( sizeof ( TaskAnnotation * ) );
813       }
814       ret[num] = task->annotations[loop];
815       num++;
816     }
817   }
818 
819   *num_ret = num;
820   return ( ret );
821 }
822 
823 
824 
825