diff options
Diffstat (limited to 'src/utils.c')
-rw-r--r-- | src/utils.c | 768 |
1 files changed, 508 insertions, 260 deletions
diff --git a/src/utils.c b/src/utils.c index b165111..fa4cefe 100644 --- a/src/utils.c +++ b/src/utils.c @@ -42,6 +42,8 @@ #include <ctype.h> #include <sys/types.h> #include <errno.h> +#include <fcntl.h> +#include <sys/wait.h> #include "calcurse.h" @@ -87,13 +89,13 @@ free_user_data (void) { day_free_list (); event_llist_free (); - event_free_bkp (ERASE_FORCE); + event_free_bkp (); apoint_llist_free (); - apoint_free_bkp (ERASE_FORCE); + apoint_free_bkp (); recur_apoint_llist_free (); recur_event_llist_free (); - recur_apoint_free_bkp (ERASE_FORCE); - recur_event_free_bkp (ERASE_FORCE); + recur_apoint_free_bkp (); + recur_event_free_bkp (); todo_free_list (); notify_free_app (); } @@ -113,7 +115,7 @@ fatalbox (const char *errmsg) if (errmsg == NULL) return; - (void)strncpy (msg, errmsg, MSGLEN); + strncpy (msg, errmsg, MSGLEN); errwin = newwin (WINROW, WINCOL, (row - WINROW) / 2, (col - WINCOL) / 2); custom_apply_attr (errwin, ATTR_HIGHEST); box (errwin, 0, 0); @@ -122,7 +124,7 @@ fatalbox (const char *errmsg) mvwprintw (errwin, 5, (WINCOL - strlen (msg)) / 2, "%s", msg); custom_remove_attr (errwin, ATTR_HIGHEST); wins_wrefresh (errwin); - (void)wgetch (errwin); + wgetch (errwin); delwin (errwin); wins_doupdate (); } @@ -140,7 +142,7 @@ warnbox (const char *msg) if (msg == NULL) return; - (void)strncpy (displmsg, msg, MSGLEN); + strncpy (displmsg, msg, MSGLEN); warnwin = newwin (WINROW, WINCOL, (row - WINROW) / 2, (col - WINCOL) / 2); custom_apply_attr (warnwin, ATTR_HIGHEST); box (warnwin, 0, 0); @@ -148,7 +150,7 @@ warnbox (const char *msg) mvwprintw (warnwin, 5, (WINCOL - strlen (displmsg)) / 2, "%s", displmsg); custom_remove_attr (warnwin, ATTR_HIGHEST); wins_wrefresh (warnwin); - (void)wgetch (warnwin); + wgetch (warnwin); delwin (warnwin); wins_doupdate (); } @@ -199,7 +201,7 @@ popup (int pop_row, int pop_col, int pop_y, int pop_x, char *title, char *msg, mvwprintw (popup_win, MSGXPOS, (pop_col - strlen (msg)) / 2, "%s", msg); custom_apply_attr (popup_win, ATTR_HIGHEST); box (popup_win, 0, 0); - (void)snprintf (label, BUFSIZ, "%s", title); + snprintf (label, BUFSIZ, "%s", title); wins_show (popup_win, label); if (hint) mvwprintw (popup_win, pop_row - 2, pop_col - (strlen (any_key) + 1), "%s", @@ -230,189 +232,6 @@ print_in_middle (WINDOW *win, int starty, int startx, int width, char *string) custom_remove_attr (win, ATTR_HIGHEST); } -/* Print the string at the desired position. */ -static void -showstring (WINDOW *win, int x, int y, char *str, int len, int scroff, - int curpos) -{ - char c = 0; - - /* print string */ - mvwaddnstr (win, y, x, &str[scroff], -1); - wclrtoeol (win); - - /* print scrolling indicator */ - if (scroff > 0 && scroff < len - col) - c = '*'; - else if (scroff > 0) - c = '<'; - else if (scroff < len - col) - c = '>'; - mvwprintw (win, y, col - 1, "%c", c); - - /* print cursor */ - wmove (win, y, curpos - scroff); - - if (curpos >= len) - waddch (win, SPACE | A_REVERSE); - else - waddch (win, str[curpos] | A_REVERSE); -} - -/* Delete a character at the given position in string. */ -static void -del_char (int pos, char *str) -{ - str += pos; - memmove (str, str + 1, strlen (str) + 1); -} - -/* Add a character at the given position in string. */ -static void -ins_char (int pos, int ch, char *str) -{ - str += pos; - memmove (str + 1, str, strlen (str) + 1); - *str = ch; -} - -static void -bell (void) -{ - printf ("\a"); -} - -/* - * Getstring allows to get user input and to print it on a window, - * even if noecho() is on. This function is also used to modify an existing - * text (the variable string can be non-NULL). - * We need to do the echoing manually because of the multi-threading - * environment, otherwise the cursor would move from place to place without - * control. - */ -enum getstr -getstring (WINDOW *win, char *str, int l, int x, int y) -{ - const int pgsize = col / 3; - - int len = strlen (str); - int curpos = len; - int scroff = 0; - int ch; - - custom_apply_attr (win, ATTR_HIGHEST); - - for (;;) { - while (curpos < scroff) - scroff -= pgsize; - while (curpos >= scroff + col - 1) - scroff += pgsize; - - showstring (win, x, y, str, len, scroff, curpos); - wins_doupdate (); - - if ((ch = wgetch (win)) == '\n') break; - switch (ch) - { - case KEY_BACKSPACE: /* delete one character */ - case 330: - case 127: - case CTRL ('H'): - if (curpos > 0) - { - del_char ((--curpos), str); - len--; - } - else - bell (); - break; - case CTRL ('D'): /* delete next character */ - if (curpos < len) - { - del_char (curpos, str); - len--; - } - else - bell (); - break; - case CTRL ('W'): /* delete a word */ - if (curpos > 0) { - while (curpos && str[curpos - 1] == ' ') - { - del_char ((--curpos), str); - len--; - } - while (curpos && str[curpos - 1] != ' ') - { - del_char ((--curpos), str); - len--; - } - } - else - bell (); - break; - case CTRL ('K'): /* delete to end-of-line */ - str[curpos] = 0; - len = curpos; - break; - case CTRL ('A'): /* go to begginning of string */ - curpos = 0; - break; - case CTRL ('E'): /* go to end of string */ - curpos = len; - break; - case KEY_LEFT: /* move one char backward */ - case CTRL ('B'): - if (curpos > 0) curpos--; - break; - case KEY_RIGHT: /* move one char forward */ - case CTRL ('F'): - if (curpos < len) curpos++; - break; - case ESCAPE: /* cancel editing */ - return (GETSTRING_ESC); - break; - default: /* insert one character */ - if (len < l - 1) - { - ins_char ((curpos++), ch, str); - len++; - } - } - } - - custom_remove_attr (win, ATTR_HIGHEST); - - return (len == 0 ? GETSTRING_RET : GETSTRING_VALID); -} - -/* Update an already existing string. */ -int -updatestring (WINDOW *win, char **str, int x, int y) -{ - int len = strlen (*str); - char *buf; - enum getstr ret; - - EXIT_IF (len + 1 > BUFSIZ, _("Internal error: line too long")); - - buf = mem_malloc (BUFSIZ); - (void)memcpy (buf, *str, len + 1); - - ret = getstring (win, buf, BUFSIZ, x, y); - - if (ret == GETSTRING_VALID) - { - len = strlen (buf); - *str = mem_realloc (*str, len + 1, 1); - EXIT_IF (*str == NULL, _("out of memory")); - (void)memcpy (*str, buf, len + 1); - } - - mem_free (buf); - return ret; -} - /* checks if a string is only made of digits */ int is_all_digit (char *string) @@ -430,8 +249,8 @@ is_all_digit (char *string) long get_item_time (long date) { - return (long)(get_item_hour (date) * HOURINSEC + - get_item_min (date) * MININSEC); + return (long)(get_item_hour(date) * HOURINSEC + + get_item_min(date) * MININSEC); } int @@ -474,7 +293,7 @@ date_sec2date_str (long sec, char *datefmt) char *datestr = (char *) mem_calloc (BUFSIZ, sizeof (char)); if (sec == 0) - (void)snprintf (datestr, BUFSIZ, "0"); + strncpy (datestr, "0", BUFSIZ); else { lt = localtime ((time_t *)&sec); @@ -529,7 +348,7 @@ update_time_in_date (long date, unsigned hr, unsigned mn) new_date = mktime (lt); EXIT_IF (new_date == -1, _("error in mktime")); - return (new_date); + return new_date; } /* @@ -558,44 +377,13 @@ get_sec_date (struct date date) date.yyyy = atoi (current_year); } long_date = date2sec (date, 0, 0); - return (long_date); + return long_date; } long min2sec (unsigned minutes) { - return (minutes * MININSEC); -} - -/* - * Checks if a time has a good format. - * The format could be either HH:MM or H:MM or MM, and we should have: - * 0 <= HH <= 24 and 0 <= MM < 999. - * This function returns 1 if the entered time is correct and in - * [h:mm] or [hh:mm] format, and 2 if the entered time is correct and entered - * in [mm] format. - */ -int -check_time (char *string) -{ - char *s = mem_strdup(string); - char *hour = strtok(s, ":"); - char *min = strtok(NULL, ":"); - int h, m; - int ret = 0; - - if (min) - { - h = atoi (hour); - m = atoi (min); - if (h >= 0 && h < 24 && m >= 0 && m < MININSEC) - ret = 1; - } - else if (strlen(s) < 4 && is_all_digit(s) && atoi(s) > 0) - ret = 2; - - mem_free(s); - return ret; + return minutes * MININSEC; } /* @@ -631,7 +419,7 @@ item_in_popup (char *saved_a_start, char *saved_a_end, char *msg, const int padl = winl - 2, padw = winw - margin_left; pad = newpad (padl, padw); - popup_win = popup (winl, winw, 1, 2, pop_title, (char *)0, 1); + popup_win = popup (winl, winw, 1, 2, pop_title, NULL, 1); if (strncmp (pop_title, _("Appointment"), 11) == 0) { mvwprintw (popup_win, margin_top, margin_left, "- %s -> %s", @@ -641,7 +429,7 @@ item_in_popup (char *saved_a_start, char *saved_a_end, char *msg, wmove (win[STA].p, 0, 0); pnoutrefresh (pad, 0, 0, margin_top + 2, margin_left, padl, winw); wins_doupdate (); - (void)wgetch (popup_win); + wgetch (popup_win); delwin (pad); delwin (popup_win); } @@ -662,7 +450,7 @@ get_today (void) day.yyyy = lt->tm_year + 1900; current_day = date2sec (day, 0, 0); - return (current_day); + return current_day; } /* Returns the current time in seconds. */ @@ -678,7 +466,7 @@ nowstr (void) static char buf[BUFSIZ]; time_t t = now (); - (void)strftime (buf, sizeof buf, "%a %b %d %T %Y", localtime (&t)); + strftime (buf, sizeof buf, "%a %b %d %T %Y", localtime (&t)); return buf; } @@ -696,7 +484,7 @@ mystrtol (const char *str) if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) EXIT (_("out of range")); - return (lval); + return lval; } /* Print the given option value with appropriate color. */ @@ -753,13 +541,13 @@ new_tempfile (const char *prefix, int trailing_len) FILE *file; if (prefix == NULL) - return (NULL); + return NULL; prefix_len = strlen (prefix); if (prefix_len + trailing_len >= BUFSIZ) - return (NULL); + return NULL; memcpy (fullname, prefix, prefix_len); - (void)memset (fullname + prefix_len, 'X', trailing_len); + memset (fullname + prefix_len, 'X', trailing_len); fullname[prefix_len + trailing_len] = '\0'; if ((fd = mkstemp (fullname)) == -1 || (file = fdopen (fd, "w+")) == NULL) { @@ -769,31 +557,13 @@ new_tempfile (const char *prefix, int trailing_len) close (fd); } ERROR_MSG (_("temporary file \"%s\" could not be created"), fullname); - return (char *)0; + return NULL; } fclose (file); return mem_strdup (fullname + prefix_len); } -/* Erase a note previously attached to a todo, event or appointment. */ -void -erase_note (char **note, enum eraseflg flag) -{ - char fullname[BUFSIZ]; - - if (*note == NULL) - return; - if (flag != ERASE_FORCE_KEEP_NOTE) - { - (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, *note); - if (unlink (fullname) != 0) - EXIT (_("could not remove note")); - } - mem_free (*note); - *note = NULL; -} - /* * Convert a string containing a date into three integers containing the year, * month and day. @@ -807,11 +577,11 @@ erase_note (char **note, enum eraseflg flag) * Returns 1 if sucessfully converted or 0 if the string is an invalid date. */ int -parse_date (char *date_string, enum datefmt datefmt, int *year, int *month, - int *day, struct date *slctd_date) +parse_date (const char *date_string, enum datefmt datefmt, int *year, + int *month, int *day, struct date *slctd_date) { - char sep = (datefmt == DATEFMT_ISO) ? '-' : '/'; - char *p; + const char sep = (datefmt == DATEFMT_ISO) ? '-' : '/'; + const char *p; int in[3] = {0, 0, 0}, n = 0; int d, m, y; @@ -888,6 +658,168 @@ parse_date (char *date_string, enum datefmt datefmt, int *year, int *month, return 1; } +/* + * Converts a time string into hours and minutes. Short forms like "23:" + * (23:00) or ":45" (0:45) are allowed. + * + * Returns 1 on success and 0 on failure. + */ +int +parse_time (const char *string, unsigned *hour, unsigned *minute) +{ + const char *p; + unsigned in[2] = {0, 0}, n = 0; + + if (!string) + return 0; + + /* parse string into in[], read up to two integers */ + for (p = string; *p; p++) + { + if (*p == ':') + { + if ((++n) > 1) + return 0; + } + else if ((*p >= '0') && (*p <= '9')) + in[n] = in[n] * 10 + (int)(*p - '0'); + else + return 0; + } + + if (n != 1 || in[0] >= DAYINHOURS || in[1] >= HOURINMIN) + return 0; + + *hour = in[0]; + *minute = in[1]; + return 1; +} + +/* + * Converts a duration string into minutes. + * + * Allowed formats (noted as regular expressions): + * + * - \d*:\d* + * - (\d*m|\d*h(|\d*m)|\d*d(|\d*m|\d*h(|\d*m))) + * - \d+ + * + * "\d" is used as a placeholder for "(0|1|2|3|4|5|6|7|8|9)". + * + * Note that this function performs an additional range check on each token to + * ensure we do not accept semantically invalid strings such as "42:23". + * + * Returns 1 on success and 0 on failure. + */ +int +parse_duration (const char *string, unsigned *duration) +{ + enum { + STATE_INITIAL, + STATE_HHMM_MM, + STATE_DDHHMM_HH, + STATE_DDHHMM_MM, + STATE_DONE + } state = STATE_INITIAL; + + const char *p; + unsigned in = 0; + unsigned dur = 0; + + if (!string || *string == '\0') + return 0; + + /* parse string using a simple state machine */ + for (p = string; *p; p++) + { + if ((*p >= '0') && (*p <= '9')) + { + if (state == STATE_DONE) + return 0; + else + in = in * 10 + (int)(*p - '0'); + } + else + { + switch (state) + { + case STATE_INITIAL: + if (*p == ':') + { + dur += in * HOURINMIN; + state = STATE_HHMM_MM; + } + else if (*p == 'd') + { + dur += in * DAYINMIN; + state = STATE_DDHHMM_HH; + } + else if (*p == 'h') + { + if (in >= DAYINHOURS) + return 0; + dur += in * HOURINMIN; + state = STATE_DDHHMM_MM; + } + else if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_DDHHMM_HH: + if (*p == 'h') + { + if (in >= DAYINHOURS) + return 0; + dur += in * HOURINMIN; + state = STATE_DDHHMM_MM; + } + else if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_DDHHMM_MM: + if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_HHMM_MM: + case STATE_DONE: + return 0; + break; + } + + in = 0; + } + } + + if ((state == STATE_HHMM_MM && in >= HOURINMIN) || + ((state == STATE_DDHHMM_HH || state == STATE_DDHHMM_MM) && in > 0)) + return 0; + + dur += in; + *duration = dur; + + return 1; +} + void str_toupper (char *s) { @@ -916,3 +848,319 @@ psleep (unsigned secs) for (unslept = sleep (secs); unslept; unslept = sleep (unslept)) ; } + +/* + * Fork and execute an external process. + * + * If pfdin and/or pfdout point to a valid address, a pipe is created and the + * appropriate file descriptors are written to pfdin/pfdout. + */ +int +fork_exec (int *pfdin, int *pfdout, const char *path, char *const *arg) +{ + int pin[2], pout[2]; + int pid; + + if (pfdin && (pipe (pin) == -1)) + return 0; + if (pfdout && (pipe (pout) == -1)) + return 0; + + if ((pid = fork ()) == 0) + { + if (pfdout) + { + if (dup2 (pout[0], STDIN_FILENO) < 0) + _exit (127); + close (pout[0]); + close (pout[1]); + } + + if (pfdin) + { + if (dup2 (pin[1], STDOUT_FILENO) < 0) + _exit (127); + close (pin[0]); + close (pin[1]); + } + + execvp (path, arg); + _exit (127); + } + else + { + if (pfdin) + close (pin[1]); + if (pfdout) + close (pout[0]); + + if (pid > 0) + { + if (pfdin) + { + fcntl (pin[0], F_SETFD, FD_CLOEXEC); + *pfdin = pin[0]; + } + if (pfdout) + { + fcntl (pout[1], F_SETFD, FD_CLOEXEC); + *pfdout = pout[1]; + } + } + else + { + if (pfdin) + close (pin[0]); + if (pfdout) + close (pout[1]); + return 0; + } + } + return pid; +} + +/* Execute an external program in a shell. */ +int +shell_exec (int *pfdin, int *pfdout, char *cmd) +{ + char *arg[] = { "/bin/sh", "-c", cmd, NULL }; + return fork_exec (pfdin, pfdout, *arg, arg); +} + +/* Wait for a child process to terminate. */ +int +child_wait (int *pfdin, int *pfdout, int pid) +{ + int stat; + + if (pfdin) + close (*pfdin); + if (pfdout) + close (*pfdout); + + waitpid (pid, &stat, 0); + return stat; +} + +/* Display "Press any key to continue..." and wait for a key press. */ +void +press_any_key (void) +{ + fflush (stdout); + fputs (_("Press any key to continue..."), stdout); + fflush (stdout); + fgetc (stdin); + fflush (stdin); + fputs ("\r\n", stdout); +} + +/* + * Display note contents if one is asociated with the currently displayed item + * (to be used together with the '-a' or '-t' flag in non-interactive mode). + * Each line begins with nbtab tabs. + * Print "No note file found", if the notefile does not exists. + * + * (patch submitted by Erik Saule). + */ +static void +print_notefile (FILE *out, char *filename, int nbtab) +{ + char path_to_notefile[BUFSIZ]; + FILE *notefile; + char linestarter[BUFSIZ]; + char buffer[BUFSIZ]; + int i; + int printlinestarter = 1; + + if (nbtab < BUFSIZ) + { + for (i = 0; i < nbtab; i++) + linestarter[i] = '\t'; + linestarter[nbtab] = '\0'; + } + else + linestarter[0] = '\0'; + + snprintf (path_to_notefile, BUFSIZ, "%s/%s", path_notes, filename); + notefile = fopen (path_to_notefile, "r"); + if (notefile) + { + while (fgets (buffer, BUFSIZ, notefile) != 0) + { + if (printlinestarter) + { + fputs (linestarter, out); + printlinestarter = 0; + } + fputs (buffer, out); + if (buffer[strlen (buffer) - 1] == '\n') + printlinestarter = 1; + } + fputs ("\n", out); + file_close (notefile, __FILE_POS__); + } + else + { + fputs (linestarter, out); + fputs (_("No note file found\n"), out); + } +} + +/* Print a formatted appointment to stdout. */ +void +print_apoint (const char *format, long day, struct apoint *apt) +{ + const char *p; + char str_start[HRMIN_SIZE], str_end[HRMIN_SIZE]; + + apoint_sec2str (apt, day, str_start, str_end); + + for (p = format; *p; p++) + { + if (*p == '%') { + p++; + switch (*p) + { + case 's': + printf ("%ld", apt->start); + break; + case 'S': + printf ("%s", str_start); + break; + case 'd': + printf ("%ld", apt->dur); + break; + case 'e': + printf ("%ld", apt->start + apt->dur); + break; + case 'E': + printf ("%s", str_end); + break; + case 'm': + printf ("%s", apt->mesg); + break; + case 'n': + printf ("%s", apt->note); + break; + case 'N': + print_notefile (stdout, apt->note, 1); + break; + case '%': + putchar ('%'); + break; + case '\0': + return; + break; + default: + putchar ('?'); + break; + } + } + else + putchar (*p); + } +} + +/* Print a formatted event to stdout. */ +void +print_event (const char *format, long day, struct event *ev) +{ + const char *p; + + for (p = format; *p; p++) + { + if (*p == '%') { + p++; + switch (*p) + { + case 'm': + printf ("%s", ev->mesg); + break; + case 'n': + printf ("%s", ev->note); + break; + case 'N': + print_notefile (stdout, ev->note, 1); + break; + case '%': + putchar ('%'); + break; + case '\0': + return; + break; + default: + putchar ('?'); + break; + } + } + else + putchar (*p); + } +} + +/* Print a formatted recurrent appointment to stdout. */ +void +print_recur_apoint (const char *format, long day, unsigned occurrence, + struct recur_apoint *rapt) +{ + struct apoint apt; + + apt.start = occurrence; + apt.dur = rapt->dur; + apt.mesg = rapt->mesg; + apt.note = rapt->note; + + print_apoint (format, day, &apt); +} + +/* Print a formatted recurrent event to stdout. */ +void +print_recur_event (const char *format, long day, struct recur_event *rev) +{ + struct event ev; + + ev.mesg = rev->mesg; + ev.note = rev->note; + + print_event (format, day, &ev); +} + +/* Print a formatted todo item to stdout. */ +void +print_todo (const char *format, struct todo *todo) +{ + const char *p; + + for (p = format; *p; p++) + { + if (*p == '%') { + p++; + switch (*p) + { + case 'p': + printf ("%d", abs (todo->id)); + break; + case 'm': + printf ("%s", todo->mesg); + break; + case 'n': + printf ("%s", todo->note); + break; + case 'N': + print_notefile (stdout, todo->note, 1); + break; + case '%': + putchar ('%'); + break; + case '\0': + return; + break; + default: + putchar ('?'); + break; + } + } + else + putchar (*p); + } +} |