scal.c (18405B)
1 #include <sys/param.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <pwd.h> 6 #include <stdlib.h> 7 #include <ctype.h> 8 #include <dirent.h> 9 #include <errno.h> 10 #include <time.h> 11 #include <curses.h> 12 13 #include "config.h" 14 15 #define ctrl(x) ((x) & 0x1f) 16 17 typedef enum {NO, YEARLY, MONTHLY, WEEKLY} Recurring; 18 19 typedef struct 20 { 21 Recurring recurring; 22 struct tm time; 23 char *description; 24 } Note ; 25 26 static void init_screen(void); 27 static void resize(void); 28 static void start(void); 29 static void print_words(void); 30 static void write_notes(void); 31 static Note ** get_notes(void); 32 static Note * get_note(char *line_buf, ssize_t line_size); 33 static char * prompt_answer(char *buf, size_t size, const char *question, const char *mask, char *prefill); 34 static void print_view(void); 35 static void print_month(const int monthadd, int *y); 36 static void combo_key(int keypress); 37 static void combo_go(int keypress); 38 static void combo_make(int keypress); 39 static void add_note(struct tm *tm); 40 static void edit_notes(void); 41 static void rm_note(struct tm *tm); 42 static int get_curnotes(void); 43 static int get_note_view_col(Recurring recurring); 44 static int has_note(struct tm *tm); 45 static int get_days(const struct tm *tm); 46 static int days_between(struct tm *tsa, struct tm *tsb); 47 static int same_day(struct tm *a, struct tm *b, Recurring recurring); 48 static int compare_note(const void *a, const void *b); 49 static void move_page_up(void); 50 static void move_page_down(void); 51 static void move_up(void); 52 static void move_down(void); 53 static void move_right(void); 54 static void move_left(void); 55 static void curdate(void); 56 static char * cpstr(char *dest, const char *src); 57 static char * replace_digits(char *buf, const char *mask, int size); 58 static char * replace_home(char *buf, const char *path, const char *userhome); 59 static void free_notes(void); 60 static void clean(void); 61 static void fatal(const char *fmt, ...); 62 63 static char *MonthName[] = {"January","February","March","April","May","June","July","August", 64 "September","October","November","December"}; 65 static char *DayName[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}; 66 static struct tm curtm, highltm; 67 static WINDOW *wwin, *vwin; 68 static int maxy, maxx, wmaxy, wmaxx, vmaxy, vmaxx, highlight, note_count=0, curnote_count=0; 69 static Note **notes, **curnotes = NULL; 70 static char file[PATH_MAX], *editor; 71 72 int 73 main(int argc, char **argv) 74 { 75 char *userhome; 76 struct passwd userinf; 77 78 editor = getenv("EDITOR"); 79 80 userinf = *getpwuid(getuid()); 81 userhome = strdup(userinf.pw_dir); 82 83 replace_home(file, WORD_FILE, userhome); 84 85 get_notes(); 86 87 curdate(); 88 init_screen(); 89 90 print_words(); 91 print_view(); 92 93 start(); 94 95 clean(); 96 } 97 98 void 99 init_screen(void) 100 { 101 initscr(); 102 noecho(); 103 start_color(); 104 use_default_colors(); 105 keypad(stdscr, TRUE); 106 curs_set(0); 107 108 clear(); 109 cbreak(); 110 111 init_pair(COLOR_BLACK, COLOR_BLACK, -1); 112 init_pair(COLOR_RED, COLOR_RED, -1); 113 init_pair(COLOR_GREEN,COLOR_GREEN, -1); 114 init_pair(COLOR_YELLOW, COLOR_YELLOW, -1); 115 init_pair(COLOR_BLUE, COLOR_BLUE, -1); 116 init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1); 117 init_pair(COLOR_CYAN, COLOR_CYAN, -1); 118 init_pair(COLOR_WHITE, COLOR_WHITE, -1); 119 120 resize(); 121 } 122 123 void 124 resize(void) 125 { 126 int startx; 127 128 getmaxyx(stdscr, maxy, maxx); 129 130 wmaxy = maxy - BORDER_SPACE_SIZE; 131 wmaxx = maxx/5 - BORDER_SPACE_SIZE > 0 ? maxx/5 - BORDER_SPACE_SIZE : 1; 132 startx = maxx > BORDER_SPACE_SIZE ? BORDER_SPACE_SIZE : 0; 133 wwin = newwin(wmaxy, wmaxx, BORDER_SPACE_SIZE, startx); 134 135 vmaxy = maxy - BORDER_SPACE_SIZE; 136 vmaxx = maxx - wmaxx - BORDER_SPACE_SIZE; 137 startx = wmaxx + BORDER_SPACE_SIZE; 138 vwin = newwin(wmaxy, vmaxx, BORDER_SPACE_SIZE, startx); 139 140 clear(); 141 refresh(); 142 } 143 144 void 145 print_words(void) 146 { 147 int y = 0; 148 149 wclear(wwin); 150 wclear(vwin); 151 152 attron(COLOR_PAIR(HIGHLIGHT_COLOR)); 153 mvprintw(0, 0, "%s %d %s %d\n", 154 DayName[highltm.tm_wday], 155 highltm.tm_mday, 156 MonthName[highltm.tm_mon], 157 highltm.tm_year + 1900); 158 attroff(COLOR_PAIR(HIGHLIGHT_COLOR)); 159 160 print_month(-1, &y); 161 print_month(0, &y); 162 print_month(1, &y); 163 164 wrefresh(wwin); 165 } 166 167 void 168 print_month(const int monthadd, int *y) 169 { 170 int wcount, today, wnote, sindex, x = 0; 171 struct tm otm = highltm; 172 173 if (monthadd >= 0) 174 (*y)+=2; 175 otm.tm_mon += monthadd; 176 otm.tm_mday = 1; 177 mktime(&otm); 178 wcount = get_days(&otm); 179 180 wattron(wwin, COLOR_PAIR(MONTH_TITLE_COLOR)); 181 mvwprintw(wwin, *y,0,"%s %d\n", 182 MonthName[otm.tm_mon], 183 otm.tm_year + 1900); 184 wattroff(wwin, COLOR_PAIR(MONTH_TITLE_COLOR)); 185 mvwprintw(wwin, (*y)+=2 ,0,"%s\n", "Su Mo Tu We Th Fr Sa"); 186 187 (*y)+=1; 188 x=otm.tm_wday*3; 189 190 for (size_t i = 0; i < wcount; ++i) { 191 otm.tm_mday=i+1; 192 today = same_day(&otm, &curtm, 0); 193 wnote = has_note(&otm); 194 195 if (today) 196 wattron(wwin, COLOR_PAIR(CURDAY_COLOR)); 197 if (wnote) 198 wattron(wwin, COLOR_PAIR(NOTED_COLOR)); 199 if (monthadd == 0 && highlight == i) { 200 wattron(wwin, A_REVERSE); 201 mvwprintw(wwin, *y, x, "%d", otm.tm_mday); 202 wattroff(wwin, A_REVERSE); 203 } else 204 mvwprintw(wwin, *y, x, "%d", otm.tm_mday); 205 if (today) 206 wattroff(wwin, COLOR_PAIR(CURDAY_COLOR)); 207 if (wnote) 208 wattroff(wwin, COLOR_PAIR(NOTED_COLOR)); 209 x+=3; 210 if (x % DAY_COLUMN_SIZE == 0 && i != wcount-1) { 211 (*y)++; 212 x = 0; 213 } 214 } 215 } 216 217 void 218 start(void) 219 { 220 int c; 221 222 while ((c = getch()) != KEY_QUIT) { 223 switch (c) { 224 case KEY_UP: 225 case KEY_VUP: 226 move_up(); 227 break; 228 case KEY_DOWN: 229 case KEY_VDOWN: 230 move_down(); 231 break; 232 case 10: 233 print_view(); 234 break; 235 case KEY_RIGHT: 236 case KEY_VRIGHT: 237 case KEY_VRIGHT_ABS: 238 move_right(); 239 break; 240 case KEY_LEFT: 241 case KEY_VLEFT: 242 case KEY_VLEFT_ABS: 243 move_left(); 244 break; 245 case KEY_PPAGE: 246 case ctrl('u'): 247 move_page_up(); 248 break; 249 case KEY_NPAGE: 250 case ctrl('d'): 251 move_page_down(); 252 break; 253 case KEY_EDIT_NOTES: 254 edit_notes(); 255 break; 256 case KEY_COMBO_GO: 257 case KEY_COMBO_MAKE: 258 combo_key(c); 259 break; 260 case KEY_RESIZE: 261 resize(); 262 break; 263 default: 264 break; 265 } 266 print_words(); 267 print_view(); 268 } 269 } 270 271 void 272 combo_key(int keypress) 273 { 274 int c; 275 276 move(maxy-1, maxx-1); 277 clrtoeol(); 278 addch(keypress); 279 280 halfdelay(10); 281 282 while ((c = getch()) != ERR) { 283 switch (keypress) { 284 case KEY_COMBO_GO: 285 combo_go(c); 286 break; 287 case KEY_COMBO_MAKE: 288 combo_make(c); 289 break; 290 default: 291 break; 292 } 293 break; 294 } 295 296 cbreak(); 297 move(maxy-1, maxx-1); 298 clrtoeol(); 299 } 300 301 void 302 combo_go(int key) 303 { 304 char pathbuf[PATH_MAX]; 305 306 switch (key) { 307 case KEY_COMBO_GO_TODAY: 308 curdate(); 309 break; 310 default: 311 break; 312 } 313 } 314 315 void 316 combo_make(int key) 317 { 318 char pathbuf[PATH_MAX]; 319 320 cbreak(); 321 322 switch (key) { 323 case KEY_COMBO_MAKE_ADD: 324 add_note(&highltm); 325 break; 326 case KEY_COMBO_MAKE_RM: 327 rm_note(&highltm); 328 break; 329 default: 330 break; 331 } 332 } 333 334 char * 335 prompt_answer(char *buf, size_t size, const char *question, const char *mask, char *prefill) 336 { 337 int c, y, x, nr, phlen = mask == NULL ? 0 : strlen(mask); 338 char placeholder[phlen]; 339 char *startfill; 340 size_t len = 0; 341 342 replace_digits(placeholder, mask, phlen); 343 344 move(maxy-1, 1); 345 clrtoeol(); 346 347 attron(COLOR_PAIR(PROMPT_COLOR)); 348 addstr(question); 349 attroff(COLOR_PAIR(PROMPT_COLOR)); 350 if (prefill != NULL) 351 startfill = prefill; 352 else if (placeholder != NULL) 353 startfill = placeholder; 354 else 355 startfill = ""; 356 357 printw(" %s", startfill); 358 359 if (prefill != NULL) { 360 len = strlen(prefill); 361 cpstr(buf, prefill); 362 } 363 364 move(maxy-1, strlen(question)+2 + len); 365 366 echo(); 367 curs_set(1); 368 369 getyx(stdscr, y, x); 370 371 while ((c = wgetch(stdscr)) != 10) { 372 move(maxy-2, 1); 373 clrtoeol(); 374 move(y,x); 375 switch (c) { 376 case KEY_BACKSPACE: 377 if (len > 0) { 378 if (len <= phlen) { 379 buf[--(len)] = placeholder[len]; 380 move(y, --x); 381 attron(COLOR_PAIR(PROMPT_COLOR)); 382 addch(buf[len]); 383 attroff(COLOR_PAIR(PROMPT_COLOR)); 384 move(y, x); 385 if (len > 0 && len <= phlen && placeholder[len] != '_') { 386 buf[--(len)] = placeholder[len]; 387 move(y, --x); 388 attron(COLOR_PAIR(PROMPT_COLOR)); 389 addch(buf[len]); 390 attroff(COLOR_PAIR(PROMPT_COLOR)); 391 move(y, x); 392 } 393 } else { 394 buf[--(len)] = '\0'; 395 move(y, --x); 396 clrtoeol(); 397 } 398 } 399 break; 400 default: 401 if (len < size) { 402 if (len < phlen) { 403 if (!isdigit(c) || (nr = mask[len] - '0') < c - '0') { 404 mvprintw(maxy-2, 1, "Only digits (max: %d)", nr); 405 move(y,x); 406 break; 407 } 408 buf[(len)++] = c; 409 move(y, ++x); 410 if (len < phlen && placeholder[len] != '_') { 411 buf[len] = placeholder[len]; 412 len++; 413 move(y, ++x); 414 } 415 } else { 416 buf[(len)++] = c; 417 move(y, ++x); 418 } 419 } else 420 clrtoeol(); 421 break; 422 } 423 } 424 noecho(); 425 curs_set(0); 426 427 buf[phlen > 0 ? phlen : len] = '\0'; 428 } 429 430 void 431 add_note(struct tm *tm) { 432 size_t len; 433 char answer[DESCLEN+20], buf[DESCLEN], rec[2], time[6], date[11], prefill[11]; 434 435 if (tm != NULL) 436 snprintf(prefill, 11, "%04d-%02d-%02d\0", 437 tm->tm_year+1900, 438 tm->tm_mon+1, 439 tm->tm_mday); 440 441 prompt_answer(rec, 1, "Recurring? (0=NO;1=YEARLY;2=MONTHLY;3=WEEKLY)", "3", NULL); 442 prompt_answer(date, 10, "Date?", "2999-19-39", prefill); 443 prompt_answer(time, 5, "Time?", "29:59", NULL); 444 prompt_answer(buf, DESCLEN, "description?", NULL, NULL); 445 snprintf(answer, DESCLEN+20, "%s|%10sT%s|%s", rec, date, time, buf); 446 447 len = strlen(answer); 448 449 if (len > 19) { 450 notes = (Note**) realloc(notes, (sizeof(Note) * (note_count + 1))); 451 if (notes == NULL) 452 fatal("Fatal: failed to reallocate bytes for notes.\n"); 453 454 notes[note_count] = get_note(answer, len); 455 note_count++; 456 write_notes(); 457 } 458 459 move(maxy-1, 1); 460 clrtoeol(); 461 move(maxy-2, 1); 462 clrtoeol(); 463 } 464 465 void 466 edit_notes(void) 467 { 468 if (editor == NULL) 469 fatal("no editor found; set EDITOR environment variable."); 470 471 sigset_t set; 472 char cmd[PATH_MAX + strlen(editor) + 4]; 473 474 endwin(); 475 476 if (sigemptyset (&set) == -1) 477 fatal("Sigemptyset failed."); 478 if (sigaddset(&set, SIGWINCH) == -1) 479 fatal("Sigaddset failed."); 480 if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) 481 fatal("Blocking sigprocmask failed."); 482 483 sprintf(cmd, "%s \"%s\"", editor, file); 484 system(cmd); 485 486 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) 487 fatal("Unblocking sigprocmask failed."); 488 489 clear(); 490 noecho(); 491 cbreak(); 492 493 free_notes(); 494 get_notes(); 495 refresh(); 496 } 497 498 void 499 rm_note(struct tm *tm) { 500 int c, y, x; 501 502 if (curnote_count == 0) 503 return; 504 505 move(BORDER_SPACE_SIZE, wmaxx + BORDER_SPACE_SIZE - 1); 506 507 curs_set(1); 508 509 getyx(stdscr, y, x); 510 511 512 while ((c = wgetch(stdscr)) != 10) { 513 switch (c) { 514 case KEY_BACKSPACE: 515 curs_set(0); 516 return; 517 case KEY_UP: 518 case KEY_VUP: 519 if (y-1 >= BORDER_SPACE_SIZE) 520 move(--y, x); 521 break; 522 case KEY_DOWN: 523 case KEY_VDOWN: 524 if (y+1 < BORDER_SPACE_SIZE + curnote_count) 525 move(++y, x); 526 break; 527 } 528 } 529 int found = 0; 530 for (int i = 0; i < note_count; i++) { 531 if (notes[i] == curnotes[y-BORDER_SPACE_SIZE]) { 532 found = 1; 533 free(notes[i]->description); 534 free(notes[i]); 535 } else if (found) 536 notes[i-1] = notes[i]; 537 } 538 note_count--; 539 write_notes(); 540 curs_set(0); 541 } 542 543 void 544 write_notes(void) 545 { 546 FILE *fp = fopen(file, "w+"); 547 548 if (!fp) 549 fatal("Error opening file '%s'\n", file); 550 551 qsort(notes, note_count, sizeof(Note*), compare_note); 552 553 for (int i = 0; i < note_count; i++) 554 fprintf(fp, "%d|%04d-%02d-%02dT%02d:%02d|%s\n", 555 notes[i]->recurring, 556 notes[i]->time.tm_year+1900, 557 notes[i]->time.tm_mon+1, 558 notes[i]->time.tm_mday, 559 notes[i]->time.tm_hour, 560 notes[i]->time.tm_min, 561 notes[i]->description); 562 fclose(fp); 563 } 564 565 Note ** 566 get_notes(void) 567 { 568 char *line_buf = NULL; 569 size_t line_buf_size = 0; 570 ssize_t line_size; 571 572 FILE *fp = fopen(file, "r"); 573 574 if (!fp) 575 fatal("Error opening file '%s'\n", file); 576 577 line_size = getline(&line_buf, &line_buf_size, fp); 578 notes = (Note**) malloc(sizeof(Note*)); 579 580 if (notes == NULL) 581 fatal("Fatal: failed to allocate bytes for notes.\n"); 582 583 while (line_size >= 0) { 584 notes = (Note**) realloc(notes, (sizeof(Note*) * (note_count + 1))); 585 if (notes == NULL) 586 fatal("Fatal: failed to reallocate bytes for notes.\n"); 587 588 notes[note_count] = get_note(line_buf, line_size); 589 590 line_size = getline(&line_buf, &line_buf_size, fp); 591 note_count++; 592 } 593 594 free(line_buf); 595 fclose(fp); 596 597 write_notes(); 598 599 return notes; 600 } 601 602 Note * 603 get_note(char *line_buf, ssize_t line_size) 604 { 605 char timestr[19]; 606 Note *note = (Note*) calloc(1, sizeof(Note)); 607 if (note == NULL) 608 fatal("Fatal: failed to allocate bytes for note.\n"); 609 610 note->description = (char*) malloc(line_size); 611 612 if (note->description == NULL) 613 fatal("Fatal: failed to allocate bytes for note.\n"); 614 615 sscanf(line_buf, 616 "%d|%[^|]|%[^\n]", 617 ¬e->recurring, 618 timestr, 619 note->description); 620 621 note->description[line_size-1] = '\0'; 622 623 if (sscanf(timestr, "%d-%d-%dT%d:%d", 624 &(note->time.tm_year), 625 &(note->time.tm_mon), 626 &(note->time.tm_mday), 627 &(note->time.tm_hour), 628 &(note->time.tm_min)) >= 3) { 629 note->time.tm_year-=1900; 630 note->time.tm_mon-=1; 631 } 632 633 return note; 634 } 635 636 void 637 print_view(void) 638 { 639 int y = -1, x = 0, color; 640 641 get_curnotes(); 642 643 for (int i = 0; i < curnote_count; i++) { 644 y++; 645 if ((color = get_note_view_col(curnotes[i]->recurring)) > -1) { 646 wattron(vwin, COLOR_PAIR(color)); 647 mvwaddch(vwin, y, x, '|'); 648 wattroff(vwin, COLOR_PAIR(color)); 649 } 650 mvwprintw(vwin, y, x+1, "%02d:%02d %s\n", 651 curnotes[i]->time.tm_hour, 652 curnotes[i]->time.tm_min, 653 curnotes[i]->description); 654 } 655 wrefresh(vwin); 656 } 657 658 int 659 get_curnotes(void) 660 { 661 if (curnote_count > 0) { 662 curnote_count = 0; 663 free(curnotes); 664 curnotes = NULL; 665 } 666 667 curnotes = (Note**) malloc(sizeof(Note*)); 668 669 if (curnotes == NULL) 670 fatal("Fatal: failed to allocate bytes for notes.\n"); 671 672 for (int i = 0; i < note_count; i++) { 673 if (same_day(&(notes[i]->time), &highltm, notes[i]->recurring)) { 674 curnotes = (Note**) realloc(curnotes, (sizeof(Note*) * (curnote_count + 1))); 675 if (curnotes == NULL) 676 fatal("Fatal: failed to reallocate bytes for notes.\n"); 677 curnotes[curnote_count] = notes[i]; 678 curnote_count++; 679 } 680 } 681 } 682 683 int 684 get_note_view_col(Recurring recurring) 685 { 686 switch (recurring) { 687 case WEEKLY: 688 return WEEKLY_VIEW_COLOR; 689 case MONTHLY: 690 return MONTHLY_VIEW_COLOR; 691 case YEARLY: 692 return YEARLY_VIEW_COLOR; 693 default: 694 return -1; 695 } 696 } 697 698 int 699 has_note(struct tm *tm) 700 { 701 for (int i = 0; i < note_count; i++) { 702 if (same_day(&(notes[i]->time), tm, notes[i]->recurring)) 703 return 1; 704 } 705 return 0; 706 } 707 708 int 709 get_days(const struct tm *tm) 710 { 711 struct tm start = *tm, end = *tm; 712 713 start.tm_mday=1; 714 end.tm_mon = start.tm_mon+1; 715 end.tm_mday = 1; 716 717 return days_between(&start, &end); 718 } 719 720 int 721 days_between(struct tm *tsa, struct tm *tsb) 722 { 723 time_t a = mktime(tsa); 724 time_t b = mktime(tsb); 725 726 return (b - a)/(60*60*24); 727 } 728 729 int 730 same_day(struct tm *a, struct tm *b, Recurring recurring) 731 { 732 if (a == NULL || b == NULL) 733 return 0; 734 735 mktime(a); 736 mktime(b); 737 738 return ((recurring == WEEKLY && a->tm_wday == b->tm_wday) || a->tm_mday == b->tm_mday ) 739 && (recurring >= MONTHLY || a->tm_mon == b->tm_mon) 740 && (recurring >= YEARLY || a->tm_year == b->tm_year); 741 } 742 743 int 744 compare_note(const void *a, const void *b) 745 { 746 int recurdif; 747 const Note *ma = *(Note**)a; 748 const Note *mb = *(Note**)b; 749 750 if (ma == NULL || mb == NULL) 751 return 0; 752 753 if (ma->recurring == mb->recurring) { 754 if (ma->time.tm_year != mb->time.tm_year) 755 return ma->time.tm_year > mb->time.tm_year; 756 if (ma->time.tm_mon != mb->time.tm_mon) 757 return ma->time.tm_mon > mb->time.tm_mon; 758 if (ma->time.tm_mday != mb->time.tm_mday) 759 return ma->time.tm_mday > mb->time.tm_mday; 760 } 761 if (ma->time.tm_hour != mb->time.tm_hour) 762 return ma->time.tm_hour > mb->time.tm_hour; 763 if (ma->time.tm_min != mb->time.tm_min) 764 return ma->time.tm_min > mb->time.tm_min; 765 766 return 0; 767 //return difftime(mktime(&ma->time), mktime(&mb->time)); 768 } 769 770 void 771 move_page_up(void) 772 { 773 highlight = 0; 774 highltm.tm_mday = highlight+1; 775 highltm.tm_mon -= 1; 776 mktime(&highltm); 777 } 778 779 void 780 move_page_down(void) 781 { 782 highlight = 0; 783 highltm.tm_mday = highlight+1; 784 highltm.tm_mon += 1; 785 mktime(&highltm); 786 } 787 788 void 789 move_up(void) 790 { 791 if (highlight > 0) { 792 highlight = MAX(highlight-DAY_COLUMN_SIZE, 0); 793 highltm.tm_mday = highlight+1; 794 mktime(&highltm); 795 } else if (highlight == 0){ 796 highltm.tm_mday -= 1; 797 mktime(&highltm); 798 highlight = highltm.tm_mday -1; 799 } 800 } 801 802 void 803 move_down(void) 804 { 805 int wcount = get_days(&highltm); 806 if (highlight < wcount-DAY_COLUMN_SIZE) { 807 highlight = MIN(highlight+DAY_COLUMN_SIZE, wcount-1); 808 highltm.tm_mday = highlight+1; 809 mktime(&highltm); 810 } else if (highlight < wcount-1) { 811 highlight = wcount-1; 812 highltm.tm_mday = highlight+1; 813 mktime(&highltm); 814 } else if (highlight == wcount-1){ 815 highlight = 0; 816 highltm.tm_mday = highlight+1; 817 highltm.tm_mon += 1; 818 mktime(&highltm); 819 } 820 } 821 822 void 823 move_right(void) 824 { 825 int wcount = get_days(&highltm); 826 if (highlight < wcount-1) { 827 highlight++; 828 highltm.tm_mday = highlight+1; 829 mktime(&highltm); 830 } else if (highlight == wcount-1){ 831 highlight = 0; 832 highltm.tm_mday = highlight+1; 833 highltm.tm_mon += 1; 834 mktime(&highltm); 835 } 836 } 837 838 void 839 move_left(void) 840 { 841 if (highlight > 0) { 842 highlight--; 843 highltm.tm_mday = highlight+1; 844 mktime(&highltm); 845 } else if (highlight == 0){ 846 highltm.tm_mday -= 1; 847 mktime(&highltm); 848 highlight = highltm.tm_mday -1; 849 } 850 } 851 852 char * 853 cpstr(char *dest, const char *src) 854 { 855 size_t cplen = strlen(src); 856 memcpy(dest, src, cplen); 857 dest[cplen] = '\0'; 858 return dest; 859 } 860 861 char * 862 replace_digits(char *buf, const char *mask, int size) { 863 for (int i = 0; i < size; i++) 864 buf[i] = isdigit(mask[i]) ? '_' : mask[i] ; 865 buf[size] = '\0'; 866 return buf; 867 } 868 869 char * 870 replace_home(char *buf, const char *path, const char *userhome) 871 { 872 char *envv = "$HOME"; 873 874 if ((strstr(path, envv) != NULL)) { 875 cpstr(buf, userhome); 876 strcat(buf, path + strlen(envv)); 877 } else 878 cpstr(buf, path); 879 return buf; 880 } 881 882 void 883 curdate(void) 884 { 885 time_t t = time(NULL); 886 curtm = *localtime(&t); 887 highltm = curtm; 888 highlight = highltm.tm_mday-1; 889 } 890 891 void 892 free_notes(void) 893 { 894 for (int i = 0; i < note_count; i++) { 895 free(notes[i]->description); 896 free(notes[i]); 897 } 898 if (notes != NULL) 899 free(notes); 900 901 if (curnotes != NULL) 902 free(curnotes); 903 904 notes = NULL; 905 curnotes = NULL; 906 note_count = 0; 907 curnote_count = 0; 908 } 909 910 void 911 clean(void) 912 { 913 clear(); 914 915 delwin(wwin); 916 delwin(vwin); 917 delwin(stdscr); 918 919 endwin(); 920 free_notes(); 921 } 922 923 void 924 fatal(const char *fmt, ...) 925 { 926 va_list ap; 927 928 va_start(ap, fmt); 929 vfprintf(stderr, fmt, ap); 930 va_end(ap); 931 932 clean(); 933 934 exit(EXIT_FAILURE); 935 }