aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils.c')
-rw-r--r--src/utils.c768
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);
+ }
+}