[refactor] validate_dyn, pledge, N & n options
[splanner.git] / splanner.c
blob87325d8d5c5041920ef6832bba133513ebf528fd
1 /*
2 * See LICENSE file for copyright and license details.
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <time.h>
9 #include <unistd.h>
11 /* typedef */
12 enum { after_last = 25, dyn = -1 };
13 enum week { Sun, Mon, Tue, Wed, Thu, Fri, Sat, e_day };
14 enum year { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, e_mon };
16 typedef struct {
17 char name[256];
18 enum week day;
19 enum year mon;
20 int st[2];
21 int dur;
22 } Task;
24 typedef struct {
25 char name[256];
26 time_t st;
27 time_t end;
28 } Task_t;
30 #include "config.h"
31 #include "util.h"
33 /* function declarations */
34 static void usage(void);
35 static char *convert_timet_to_str(const time_t, const char);
36 static time_t convert_hour_min_timet(const int*);
37 static time_t adj_st(const int*, const time_t);
38 static time_t adj_end(const time_t, int);
39 static int accepted_task(Task);
40 static void gather_info(size_t*, size_t*, size_t);
41 static Task *create_acct(size_t, size_t*, size_t);
42 static Task_t *create_rett(Task*, size_t*);
43 static int validate_fixed(size_t*, size_t*, Task_t*, Task*);
44 static int validate_dyn(size_t*, Task_t*, Task*);
45 static void print_t(const char);
47 /* function implementations */
48 static void
49 usage(void)
51 die("usage: splanner -[AaNnv]\noptions:\n \
52 -A print all tasks, 12-hour clock format.\n \
53 -a print all tasks.\n \
54 -N print all upcoming tasks, 12-hour clock format.\n \
55 -n print all upcoming tasks.\n \
56 -v print version.");
59 static char *
60 convert_timet_to_str(const time_t input_time, const char option)
62 char *result;
63 size_t result_size = (size_t)32;
64 struct tm new_time = *localtime(&input_time);
66 result = ecalloc(result_size, sizeof(char));
67 if ((option == 'A' || option == 'N') && (new_time.tm_hour > 12)){
68 new_time.tm_hour = new_time.tm_hour - 12;
70 (void)snprintf(result, result_size, "%.2d:%.2d",
71 new_time.tm_hour, new_time.tm_min);
72 return result;
75 static time_t
76 convert_hour_min_timet(const int *arr)
78 const int hours = arr[0];
79 const int minutes = arr[1];
80 time_t now;
81 time_t converted;
82 struct tm tmnow;
84 (void)time(&now);
85 tmnow = *localtime(&now);
86 tmnow.tm_hour = hours;
87 tmnow.tm_min = minutes;
88 tmnow.tm_sec = 0;
89 converted = mktime(&tmnow);
90 return converted;
93 static time_t
94 adj_st(const int *config_start_time_array, time_t last_task_endtime)
96 time_t result;
97 int config_hours = config_start_time_array[0];
98 /* after_last */
99 if (config_hours == (int)after_last) {
100 result = last_task_endtime;
101 /* fixed start */
102 } else {
103 result = convert_hour_min_timet(config_start_time_array);
105 return result;
108 static time_t
109 adj_end(const time_t start_time, int duration)
111 time_t returned_time;
112 duration = duration * 60;
113 returned_time = start_time + duration;
114 return returned_time;
117 static int
118 accepted_task(Task tested_task)
120 const time_t current_time = time(NULL);
121 const struct tm current_date = *localtime(&current_time);
122 const int t_day = current_date.tm_wday;
123 const int t_mon = current_date.tm_mon;
125 if (
126 ((tested_task.day == e_day) &&
127 (tested_task.mon == e_mon)) ||
129 (((int)tested_task.day == t_day) &&
130 (tested_task.mon == e_mon)) ||
132 (((int)tested_task.day == t_day) &&
133 ((int)tested_task.mon == t_mon))
135 return 0;
136 } else {
137 return -1;
141 static void
142 gather_info(size_t *acctl, size_t *fixedl, size_t config_l)
144 /* count accepted tasks */
145 /* count fixed tasks */
146 size_t i;
147 for (i=0; i<config_l; i++) {
148 if (accepted_task(config_tasks[i]) == 0) {
149 (*acctl)++;
150 if (config_tasks[i].st[0] < (int)after_last) {
151 (*fixedl)++;
157 static Task *
158 create_acct(size_t acctl, size_t *tnmax, size_t config_l)
160 /* copy accepted from config_tasks */
161 /* set max task name length */
162 Task *acct;
163 acct = ecalloc(acctl, sizeof(Task));
165 size_t i;
166 size_t a = 0;
168 for (i=0; i<config_l; i++){
169 if (accepted_task(config_tasks[i]) == 0) {
170 acct[a] = config_tasks[i];
171 if (strlen(acct[a].name) > *tnmax) {
172 *tnmax = strlen(acct[a].name);
174 a++;
177 return acct;
180 static Task_t *
181 create_rett(Task *acct, size_t *acctl)
183 /* copy accepted tasks to returned Task_t array */
184 size_t i;
185 Task_t *rett;
186 rett = ecalloc(*acctl, sizeof(Task_t));
188 for (i=0; i<*acctl; i++) {
189 /* copy task name */
190 // rett[i].name = ecalloc(strlen(acct[i].name) + 1, sizeof(char));
191 memcpy(rett[i].name, acct[i].name, strlen(acct[i].name) + 1);
193 /* create start time */
194 if (i == 0) { /* first task has no previous end time task */
195 rett[i].st = adj_st(acct[i].st,0);
196 } else {
197 rett[i].st = adj_st(acct[i].st, rett[i-1].end);
200 /* create end time */
201 rett[i].end = adj_end(rett[i].st, acct[i].dur);
203 /* chech previous task if dynamic */
204 if (i > 0){
205 if ((acct[i-1].dur == (int)dyn) &&
206 (acct[i].st[0] == (int)after_last)){
207 die("[FAILED] %s (dyn) --> %s (dyn)\ndynamic duration must be followed by fixed task start time.", acct[i-1].name, acct[i].name);
209 if (acct[i-1].dur == (int)dyn) {
210 rett[i-1].end = rett[i].st;
214 /* last task duration */
215 rett[(*acctl)-1].end = rett[0].st;
218 return rett;
221 static int
222 validate_fixed(size_t *fixedl, size_t *acctl, Task_t *rett, Task *acct)
224 Task *fixed;
225 int *btwa;
226 int *btwp;
227 double dfix;
228 int dmin = 60 * 24;
229 int dsec = dmin * 60;
230 // int tfix = 0;
231 int fail = 0; /* to print all found errors */
233 int fixdie = 0;
234 int tdur = 0;
235 size_t btwal;
236 size_t i, a, x;
238 fixed = ecalloc(*fixedl, sizeof(Task));
239 a = 0;
240 x = 0;
241 btwal = (*fixedl) - 1;
242 btwa = ecalloc(btwal, sizeof(int));
243 btwp = ecalloc(btwal, sizeof(int));
245 for (i=0; i<*acctl; i++){
246 if (acct[i].st[0] < (int)after_last) { /* fixed start */
247 if (a != 0){
248 x++;
250 if (a < btwal) {
251 btwa[x] = 0; /* start new btw 2 fixes */
253 fixed[a] = acct[i];
254 a++;
255 } else { /* after last */
256 // btwa[x] += acct[i].dur;
257 btwa[x] += (rett[i+1].st - rett[i].st)/60;
260 if (i< (*acctl)-1) {
261 tdur += (rett[i+1].st - rett[i].st)/60;
262 } else if (i == (*acctl)-1) {
263 tdur += (rett[0].st + dsec - rett[i].st)/60;
267 /* show fixes tasks and possible duration between them */
268 // for (i=0; i<btwal; i++) {
269 // if (i < btwal){
270 // printf("%s -> %s %d\n", fixed[i].name, fixed[i+1].name, btwa[i]);
271 // }
272 // }
273 // printf("total need fix = %d\n", tfix);
275 /* calculate between each 2 fixed start times */
276 /* for each 2 calculate max duration between them */
277 for (i=(size_t)1; i<*fixedl; i++){
278 dfix = difftime(convert_hour_min_timet(fixed[i].st),
279 convert_hour_min_timet(fixed[i-1].st) +
280 (fixed[i-1].dur * 60)) / 60;
281 btwp[i-1] = (int)dfix;
285 * calculate actual sum duration of between tasks *
286 * compare 2 arrays btwa & btwp fail if not equal *
287 for (i=0; i<btwal; i++) {
288 if (btwa[i] != btwp[i]) {
289 fixdie = 1;
290 printf("[\033[33mfix\033[0m] \
291 tasks between [%s] and [%s]: possible = %d, actual = %d\n",
292 fixed[i].name,fixed[i+1].name,btwp[i], btwa[i]);
297 // TODO move all cases before btwa and btwp
298 // dynamic duration must be filled for checking
300 /* fail cases */
301 FAIL_IF (fixdie == 1,"please fix tasks duration between fixed tasks");
302 // FAIL_IF (tdur != dmax, "Total duration must be 1400 min.");
304 /* calculate acctual duration */
305 // for (i=0; i<acctl; i++) {
306 // if (i< acctl-1){
307 // btwa[i] = (rett[i+1].st - rett[i].st)/60;
308 // } else if (i == acctl-1) {
309 // btwa[i] = (rett[0].st+dsec- rett[i].st)/60;
310 // }
311 // printf("%d, ", btwa[i]);
312 // }
313 // int tfix = 0;
314 // tfix += tduration;
316 /* XXX VALIDATION XXX */
317 /* copy fixed from accepted */
318 /* count acctual duration */
319 /* count total duration */
321 free(btwa);
322 free(btwp);
323 free(fixed);
325 if (fail > 0)
326 return -1;
328 return 0;
331 static int
332 validate_dyn(size_t *acctl, Task_t *rett, Task *acct)
335 * case 0: between 2 fixes and no dynamic -> less than possible
336 * case 1: dynamic duration is 0 or less
339 const int dmin = 60 * 24;
340 const int dsec = dmin * 60;
341 time_t tduration = 0;
342 int status = 0; /* to print all found errors */
343 size_t i;
345 for (i=0; i<*acctl; i++) {
346 if (i < (*acctl)-1){
347 tduration = (rett[i+1].st - rett[i].st)/60;
348 } else if (i == (*acctl)-1) {
349 tduration = (rett[0].st + dsec - rett[i].st)/60;
352 if ((tduration <= 0) ||
353 (acct[i].dur != (int)dyn && (int)tduration != acct[i].dur)) {
354 printf("[fix] %s duration is %ld min\n",
355 rett[i].name, (long)tduration);
356 status = -1;
359 return status;
362 static void
363 print_t(const char print_m)
365 Task *acct;
366 Task_t *rett;
367 char *st_str;
368 char *end_str;
369 size_t *acctl;
370 size_t *fixedl;
371 size_t *tnmax;
372 size_t i;
374 const int dmin = 60 * 24;
375 const int dsec = dmin * 60;
376 const time_t now_seconds = time(NULL);
377 size_t config_l = LEN(config_tasks);
378 time_t tduration = 0;
380 acctl = ecalloc((size_t)1, sizeof(size_t));
381 fixedl = ecalloc((size_t)1, sizeof(size_t));
382 tnmax = ecalloc((size_t)1, sizeof(size_t));
384 gather_info(acctl, fixedl, config_l);
385 acct = create_acct(*acctl, tnmax, config_l);
386 rett = create_rett(acct, acctl);
388 if (validate_dyn(acctl, rett, acct) < 0)
389 die("validation dynamic");
391 if (validate_fixed(fixedl, acctl, rett, acct) < 0)
392 die("validation fixed");
394 for (i=0; i<*acctl; i++) {
395 st_str = convert_timet_to_str(rett[i].st, print_m);
396 end_str = convert_timet_to_str(rett[i].end, print_m);
398 if (i < (*acctl)-1){
399 tduration = (rett[i+1].st - rett[i].st)/60;
400 } else if (i == (*acctl)-1) {
401 tduration = (rett[0].st + dsec - rett[i].st)/60;
404 if (print_m == 'a' || print_m == 'A') {
405 printf("%*s %s -> %s\t%ld\n",
406 ~(int)*tnmax,
407 rett[i].name,
408 st_str,
409 end_str,
410 (long)tduration);
412 } else if (print_m == 'n' || print_m == 'N' || print_m == 's') {
413 if (rett[i].end > now_seconds){
414 printf("%*s %s -> %s\t%ld\n",
415 ~(int)*tnmax,
416 rett[i].name,
417 st_str,
418 end_str,
419 (long)tduration);
420 if (print_m == 's') {
421 free(st_str);
422 free(end_str);
423 break;
428 free(st_str);
429 free(end_str);
432 free(acct);
433 free(rett);
434 free(acctl);
435 free(fixedl);
436 free(tnmax);
440 main(int argc, char *argv[])
442 #ifdef __OpenBSD__
443 if (pledge("stdio", NULL) == -1)
444 die("pledge");
445 #endif /* __OpenBSD__ */
446 if (argc == 1) {
447 print_t('s');
448 } else if (argc == 2) {
449 if (strcmp("-v", argv[1]) == 0){
450 die("splanner-"VERSION);
451 } else if (
452 strcmp("-a", argv[1]) == 0 ||
453 strcmp("-A", argv[1]) == 0 ||
454 strcmp("-N", argv[1]) == 0 ||
455 strcmp("-n", argv[1]) == 0) {
456 print_t(argv[1][1]);
457 } else {
458 usage();
460 } else {
461 usage();
463 return 0;