aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--doc/manual.txt3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/calcurse.c7
-rw-r--r--src/calcurse.h33
-rw-r--r--src/day.c76
-rw-r--r--src/getstring.c299
-rw-r--r--src/help.c13
-rw-r--r--src/io.c5
-rw-r--r--src/keys.c11
-rw-r--r--src/recur.c4
-rw-r--r--src/todo.c57
-rw-r--r--src/utf8.c344
-rw-r--r--src/utils.c290
-rw-r--r--src/wins.c61
15 files changed, 969 insertions, 239 deletions
diff --git a/TODO b/TODO
index b28f33d..a3dd91e 100644
--- a/TODO
+++ b/TODO
@@ -9,9 +9,6 @@ feature added in calcurse which does not appear in this list.
High
----
-* Add support for UTF-8
-* Implement the send-item functionality to export a single item and pipe it to
- an external process
* Add a key binding to toggle between visible/hidden tasks inside todo panel
* Add an optional argument to the --next flag to check for next appointment
starting from the specified time
diff --git a/doc/manual.txt b/doc/manual.txt
index c0a992d..6bf7637 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -113,8 +113,7 @@ library already installed on your computer, but if not, you can find it at the
following url: http://ftp.gnu.org/pub/gnu/ncurses/
NOTE: It is also possible to link `calcurse` against the `ncursesw` library
- (ncurses with support for unicode). However, UTF-8 is not yet supported
- by `calcurse`.
+ (ncurses with support for unicode).
[[install_requirements_gettext]]
gettext library
diff --git a/src/Makefile.am b/src/Makefile.am
index faacf46..8fff8e3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,6 +14,7 @@ calcurse_SOURCES = \
custom.c \
day.c \
event.c \
+ getstring.c \
help.c \
io.c \
keys.c \
@@ -22,6 +23,7 @@ calcurse_SOURCES = \
recur.c \
sigs.c \
todo.c \
+ utf8.c \
utils.c \
vars.c \
wins.c \
diff --git a/src/calcurse.c b/src/calcurse.c
index 662f185..0ae7e9a 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -368,6 +368,13 @@ main (int argc, char **argv)
do_storage = 1;
break;
+ case KEY_PIPE_ITEM:
+ if (wins_slctd () == APP && apoint_hilt () != 0)
+ day_pipe_item (&conf);
+ else if (wins_slctd () == TOD && todo_hilt () != 0)
+ todo_pipe_item ();
+ break;
+
case KEY_RAISE_PRIORITY:
case KEY_LOWER_PRIORITY:
if (wins_slctd () == TOD && todo_hilt () != 0)
diff --git a/src/calcurse.h b/src/calcurse.h
index e3822ef..b94da61 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -200,6 +200,15 @@
#define TOSTRING(x) STRINGIFY(x)
#define __FILE_POS__ __FILE__ ":" TOSTRING(__LINE__)
+#define UTF8_MAXLEN 6
+#define UTF8_LENGTH(ch) ((unsigned char)ch >= 0xFC ? 6 : \
+ ((unsigned char)ch >= 0xF8 ? 5 : \
+ ((unsigned char)ch >= 0xF0 ? 4 : \
+ ((unsigned char)ch >= 0xE0 ? 3 : \
+ ((unsigned char)ch >= 0xC0 ? 2 : 1)))))
+#define UTF8_ISCONT(ch) ((unsigned char)ch >= 0x80 && \
+ (unsigned char)ch <= 0xBF)
+
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
@@ -392,6 +401,7 @@ enum key {
KEY_DEL_ITEM,
KEY_EDIT_ITEM,
KEY_VIEW_ITEM,
+ KEY_PIPE_ITEM,
KEY_FLAG_ITEM,
KEY_REPEAT_ITEM,
KEY_EDIT_NOTE,
@@ -628,6 +638,7 @@ struct day_item *day_get_item (int);
int day_item_nb (long, int, int);
void day_edit_note (char *);
void day_view_note (char *);
+void day_pipe_item (struct conf *);
/* dmon.c */
void dmon_start (int);
@@ -650,6 +661,10 @@ void event_paste_item (void);
void help_wins_init (struct scrollwin *, int, int, int, int);
void help_screen (void);
+/* getstring.c */
+enum getstr getstring (WINDOW *, char *, int, int, int);
+int updatestring (WINDOW *, char **, int, int);
+
/* io.c */
unsigned io_fprintln (const char *, const char *, ...);
void io_init (char *, char *);
@@ -778,6 +793,8 @@ struct recur_apoint *recur_apoint_scan (FILE *, struct tm, struct tm,
struct recur_event *recur_event_scan (FILE *, struct tm, int, char,
int, struct tm, char *,
llist_t *);
+void recur_apoint_write (struct recur_apoint *, FILE *);
+void recur_event_write (struct recur_event *, FILE *);
void recur_save_data (FILE *);
unsigned recur_item_inday (long, llist_t *, int, int, long, long);
unsigned recur_apoint_inday(struct recur_apoint *, long);
@@ -814,6 +831,7 @@ int todo_hilt_pos (void);
char *todo_saved_mesg (void);
void todo_new_item (void);
struct todo *todo_add (char *, int, char *);
+void todo_write (struct todo *, FILE *);
void todo_flag (void);
void todo_delete (struct conf *);
void todo_chg_priority (int);
@@ -821,9 +839,14 @@ void todo_edit_item (void);
void todo_update_panel (int);
void todo_edit_note (char *);
void todo_view_note (char *);
+void todo_pipe_item (void);
void todo_init_list (void);
void todo_free_list (void);
+/* utf8.c */
+int utf8_width (char *);
+int utf8_strwidth (char *);
+
/* utils.c */
void exit_calcurse (int) __attribute__((__noreturn__));
void free_user_data (void);
@@ -833,8 +856,6 @@ void status_mesg (char *, char *);
void erase_window_part (WINDOW *, int, int, int, int);
WINDOW *popup (int, int, int, int, char *, char *, int);
void print_in_middle (WINDOW *, int, int, int, char *);
-enum getstr getstring (WINDOW *, char *, int, int, int);
-int updatestring (WINDOW *, char **, int, int);
int is_all_digit (char *);
long get_item_time (long);
int get_item_hour (long);
@@ -862,6 +883,10 @@ int parse_date (char *, enum datefmt, int *, int *, int *,
void str_toupper (char *);
void file_close (FILE *, const char *);
void psleep (unsigned);
+int fork_exec (int *, int *, const char *, char *const *);
+int shell_exec (int *, int *, char *);
+int child_wait (int *, int *, int);
+void press_any_key (void);
/* vars.c */
extern int col, row;
@@ -915,7 +940,9 @@ void wins_update_border (void);
void wins_update_panels (void);
void wins_update (void);
void wins_reset (void);
-void wins_launch_external (const char *, const char *);
+void wins_prepare_external (void);
+void wins_unprepare_external (void);
+void wins_launch_external (char *, char *);
void wins_status_bar (void);
void wins_erase_status_bar (void);
void wins_other_status_page (int);
diff --git a/src/day.c b/src/day.c
index 4c73f43..ffbe567 100644
--- a/src/day.c
+++ b/src/day.c
@@ -347,14 +347,15 @@ display_item_date (int incolor, struct apoint *i, int type, long date,
* Print an item description in the corresponding panel window.
*/
static void
-display_item (int incolor, char *msg, int recur, int note, int len, int y,
+display_item (int incolor, char *msg, int recur, int note, int width, int y,
int x)
{
WINDOW *win;
int ch_recur, ch_note;
- char buf[len];
+ char buf[width * UTF8_MAXLEN];
+ int i;
- if (len <= 0)
+ if (width <= 0)
return;
win = apad.ptrwin;
@@ -362,12 +363,20 @@ display_item (int incolor, char *msg, int recur, int note, int len, int y,
ch_note = (note) ? '>' : ' ';
if (incolor == 0)
custom_apply_attr (win, ATTR_HIGHEST);
- if (strlen (msg) < len)
+ if (utf8_strwidth (msg) < width)
mvwprintw (win, y, x, " %c%c%s", ch_recur, ch_note, msg);
else
{
- (void)strncpy (buf, msg, len - 1);
- buf[len - 1] = '\0';
+ for (i = 0; msg[i] && width > 0; i++)
+ {
+ if (!UTF8_ISCONT (msg[i]))
+ width -= utf8_width (&msg[i]);
+ buf[i] = msg[i];
+ }
+ if (i)
+ buf[i - 1] = 0;
+ else
+ buf[0] = 0;
mvwprintw (win, y, x, " %c%c%s...", ch_recur, ch_note, buf);
}
if (incolor == 0)
@@ -1089,3 +1098,58 @@ day_view_note (char *pager)
(void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, p->note);
wins_launch_external (fullname, pager);
}
+
+/* Pipe an appointment or event to an external program. */
+void
+day_pipe_item (struct conf *conf)
+{
+ char cmd[BUFSIZ] = "";
+ int pout;
+ int pid;
+ FILE *fpout;
+ int item_num;
+ long date;
+ struct day_item *p;
+ struct recur_apoint *ra;
+ struct apoint *a;
+ struct recur_event *re;
+ struct event *e;
+
+ status_mesg (_("Pipe item to external command:"), "");
+ if (getstring (win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
+ return;
+
+ wins_prepare_external ();
+ if ((pid = shell_exec (NULL, &pout, cmd)))
+ {
+ fpout = fdopen (pout, "w");
+
+ item_num = apoint_hilt ();
+ p = day_get_item (item_num);
+ date = calendar_get_slctd_day_sec ();
+ switch (p->type)
+ {
+ case RECUR_EVNT:
+ re = recur_get_event (date, day_item_nb (date, item_num, RECUR_EVNT));
+ recur_event_write (re, fpout);
+ break;
+ case EVNT:
+ e = event_get (date, day_item_nb (date, item_num, EVNT));
+ event_write (e, fpout);
+ break;
+ case RECUR_APPT:
+ ra = recur_get_apoint (date, day_item_nb (date, item_num, RECUR_APPT));
+ recur_apoint_write (ra, fpout);
+ break;
+ case APPT:
+ a = apoint_get (date, day_item_nb (date, item_num, APPT));
+ apoint_write (a, fpout);
+ break;
+ }
+
+ fclose (fpout);
+ child_wait (NULL, &pout, pid);
+ press_any_key ();
+ }
+ wins_unprepare_external ();
+}
diff --git a/src/getstring.c b/src/getstring.c
new file mode 100644
index 0000000..8633ef3
--- /dev/null
+++ b/src/getstring.c
@@ -0,0 +1,299 @@
+/*
+ * Calcurse - text-based organizer
+ *
+ * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Send your feedback or comments to : misc@calcurse.org
+ * Calcurse home page : http://calcurse.org
+ *
+ */
+
+#include "calcurse.h"
+
+struct getstr_charinfo {
+ unsigned int offset, dpyoff;
+};
+
+struct getstr_status {
+ char *s;
+ struct getstr_charinfo *ci;
+ int pos, len;
+ int scrpos;
+};
+
+/* Print the string at the desired position. */
+static void
+getstr_print (WINDOW *win, int x, int y, struct getstr_status *st)
+{
+ char c = 0;
+
+ /* print string */
+ mvwaddnstr (win, y, x, &st->s[st->ci[st->scrpos].offset], -1);
+ wclrtoeol (win);
+
+ /* print scrolling indicator */
+ if (st->scrpos > 0 && st->ci[st->len].dpyoff -
+ st->ci[st->scrpos].dpyoff > col - 2)
+ c = '*';
+ else if (st->scrpos > 0)
+ c = '<';
+ else if (st->ci[st->len].dpyoff - st->ci[st->scrpos].dpyoff > col - 2)
+ c = '>';
+ mvwprintw (win, y, col - 2, " %c", c);
+
+ /* print cursor */
+ wmove (win, y, st->ci[st->pos].dpyoff - st->ci[st->scrpos].dpyoff);
+ wchgat (win, 1, A_REVERSE, COLR_CUSTOM, NULL);
+}
+
+/* Delete a character at the given position in string. */
+static void
+getstr_del_char (struct getstr_status *st)
+{
+ char *str = st->s + st->ci[st->pos].offset;
+ int cl = st->ci[st->pos + 1].offset - st->ci[st->pos].offset;
+ int cw = st->ci[st->pos + 1].dpyoff - st->ci[st->pos].dpyoff;
+ int i;
+
+ memmove (str, str + cl, strlen (str) + 1);
+
+ st->len--;
+ for (i = st->pos; i <= st->len; i++)
+ {
+ st->ci[i].offset = st->ci[i + 1].offset - cl;
+ st->ci[i].dpyoff = st->ci[i + 1].dpyoff - cw;
+ }
+}
+
+/* Add a character at the given position in string. */
+static void
+getstr_ins_char (struct getstr_status *st, char *c)
+{
+ char *str = st->s + st->ci[st->pos].offset;
+ int cl = UTF8_LENGTH (c[0]);
+ int cw = utf8_width (c);
+ int i;
+
+ memmove (str + cl, str, strlen (str) + 1);
+ for (i = 0; i < cl; i++, str++)
+ *str = c[i];
+
+ for (i = st->len; i >= st->pos; i--)
+ {
+ st->ci[i + 1].offset = st->ci[i].offset + cl;
+ st->ci[i + 1].dpyoff = st->ci[i].dpyoff + cw;
+ }
+ st->len++;
+}
+
+static void
+bell (void)
+{
+ printf ("\a");
+}
+
+/* Initialize getstring data structure. */
+static void
+getstr_init (struct getstr_status *st, char *str, struct getstr_charinfo *ci)
+{
+ int width;
+
+ st->s = str;
+ st->ci = ci;
+
+ st->len = width = 0;
+ while (*str)
+ {
+ st->ci[st->len].offset = str - st->s;
+ st->ci[st->len].dpyoff = width;
+
+ st->len++;
+ width += utf8_width (str);
+ str += UTF8_LENGTH (*str);
+ }
+ st->ci[st->len].offset = str - st->s;
+ st->ci[st->len].dpyoff = width;
+
+ st->pos = st->len;
+ st->scrpos = 0;
+}
+
+/* Scroll left/right if the cursor moves outside the window range. */
+static void
+getstr_fixscr (struct getstr_status *st)
+{
+ const int pgsize = col / 3;
+ int pgskip;
+
+ while (st->pos < st->scrpos)
+ {
+ pgskip = 0;
+ while (pgskip < pgsize && st->scrpos > 0)
+ {
+ st->scrpos--;
+ pgskip += st->ci[st->scrpos + 1].dpyoff - st->ci[st->scrpos].dpyoff;
+ }
+ }
+ while (st->ci[st->pos].dpyoff - st->ci[st->scrpos].dpyoff > col - 2)
+ {
+ pgskip = 0;
+ while (pgskip < pgsize && st->scrpos < st->len)
+ {
+ pgskip += st->ci[st->scrpos + 1].dpyoff - st->ci[st->scrpos].dpyoff;
+ st->scrpos++;
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ struct getstr_status st;
+ struct getstr_charinfo ci[l + 1];
+
+ int ch, k;
+ char c[UTF8_MAXLEN];
+
+ getstr_init (&st, str, ci);
+ custom_apply_attr (win, ATTR_HIGHEST);
+
+ for (;;) {
+ getstr_fixscr (&st);
+ getstr_print (win, x, y, &st);
+ wins_doupdate ();
+
+ if ((ch = wgetch (win)) == '\n') break;
+ switch (ch)
+ {
+ case KEY_BACKSPACE: /* delete one character */
+ case 330:
+ case 127:
+ case CTRL ('H'):
+ if (st.pos > 0)
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ else
+ bell ();
+ break;
+ case CTRL ('D'): /* delete next character */
+ if (st.pos < st.len)
+ getstr_del_char (&st);
+ else
+ bell ();
+ break;
+ case CTRL ('W'): /* delete a word */
+ if (st.pos > 0) {
+ while (st.pos && st.s[st.ci[st.pos - 1].offset] == ' ')
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ while (st.pos && st.s[st.ci[st.pos - 1].offset] != ' ')
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ }
+ else
+ bell ();
+ break;
+ case CTRL ('K'): /* delete to end-of-line */
+ st.s[st.ci[st.pos].offset] = 0;
+ st.len = st.pos;
+ break;
+ case CTRL ('A'): /* go to begginning of string */
+ st.pos = 0;
+ break;
+ case CTRL ('E'): /* go to end of string */
+ st.pos = st.len;
+ break;
+ case KEY_LEFT: /* move one char backward */
+ case CTRL ('B'):
+ if (st.pos > 0) st.pos--;
+ break;
+ case KEY_RIGHT: /* move one char forward */
+ case CTRL ('F'):
+ if (st.pos < st.len) st.pos++;
+ break;
+ case ESCAPE: /* cancel editing */
+ return (GETSTRING_ESC);
+ break;
+ default: /* insert one character */
+ c[0] = ch;
+ for (k = 1; k < MIN (UTF8_LENGTH (c[0]), UTF8_MAXLEN); k++)
+ c[k] = (unsigned char)wgetch (win);
+ if (st.ci[st.len].offset + k < l)
+ {
+ getstr_ins_char (&st, c);
+ st.pos++;
+ }
+ }
+ }
+
+ custom_remove_attr (win, ATTR_HIGHEST);
+
+ return (st.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;
+}
diff --git a/src/help.c b/src/help.c
index 3f4f7c7..7c43d44 100644
--- a/src/help.c
+++ b/src/help.c
@@ -56,6 +56,7 @@ typedef enum
HELP_EXPORT,
HELP_DISPLACEMENT,
HELP_VIEW,
+ HELP_PIPE,
HELP_TAB,
HELP_GOTO,
HELP_DELETE,
@@ -296,6 +297,10 @@ wanted_page (int ch)
page = HELP_VIEW;
break;
+ case KEY_PIPE_ITEM:
+ page = HELP_PIPE;
+ break;
+
case KEY_RAISE_PRIORITY:
case KEY_LOWER_PRIORITY:
page = HELP_PRIORITY;
@@ -451,6 +456,14 @@ help_screen (void)
"Calcurse screen."),
keys_action_firstkey (KEY_VIEW_ITEM));
+ hscr[HELP_PIPE].title = _("Pipe\n");
+ (void)snprintf (hscr[HELP_PIPE].text, HELPTEXTSIZ,
+ _("Pipe the selected item to an external program.\n"
+ "\nPress the '%s' key to pipe the currently selected appointment or\n"
+ "todo entry to an external program.\n"
+ "\nYou will be driven back to calcurse as soon as the program exits.\n"),
+ keys_action_firstkey (KEY_PIPE_ITEM));
+
hscr[HELP_TAB].title = _("Tab\n");
(void)snprintf (hscr[HELP_TAB].text, HELPTEXTSIZ,
_("Switch between panels.\n"
diff --git a/src/io.c b/src/io.c
index 74a8089..09c2934 100644
--- a/src/io.c
+++ b/src/io.c
@@ -1024,10 +1024,7 @@ io_save_todo (void)
LLIST_FOREACH (&todolist, i)
{
struct todo *todo = LLIST_TS_GET_DATA (i);
- if (todo->note)
- (void)fprintf (fp, "[%d]>%s %s\n", todo->id, todo->note, todo->mesg);
- else
- (void)fprintf (fp, "[%d] %s\n", todo->id, todo->mesg);
+ todo_write (todo, fp);
}
file_close (fp, __FILE_POS__);
diff --git a/src/keys.c b/src/keys.c
index 12bdd5c..1a46530 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -81,16 +81,17 @@ static struct keydef_s keydef[NBKEYS] = {
{"generic-scroll-up", "C-p"},
{"generic-goto-today", "C-g"},
- {"move-right", "l L"},
- {"move-left", "h H"},
- {"move-down", "j J"},
- {"move-up", "k K"},
+ {"move-right", "l L RGT"},
+ {"move-left", "h H LFT"},
+ {"move-down", "j J DWN"},
+ {"move-up", "k K UP"},
{"start-of-week", "0"},
{"end-of-week", "$"},
{"add-item", "a A"},
{"del-item", "d D"},
{"edit-item", "e E"},
{"view-item", "v V"},
+ {"pipe-item", "|"},
{"flag-item", "!"},
{"repeat", "r R"},
{"edit-note", "n N"},
@@ -567,6 +568,8 @@ keys_popup_info (enum key key)
_("Flag the currently selected item as important.");
info[KEY_REPEAT_ITEM] =
_("Repeat an item");
+ info[KEY_PIPE_ITEM] =
+ _("Pipe the currently selected item to an external program.");
info[KEY_EDIT_NOTE] =
_("Attach (or edit if one exists) a note to the currently selected item");
info[KEY_VIEW_NOTE] =
diff --git a/src/recur.c b/src/recur.c
index 430e543..b757400 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -450,7 +450,7 @@ recur_event_scan (FILE *f, struct tm start, int id, char type, int freq,
}
/* Writting of a recursive appointment into file. */
-static void
+void
recur_apoint_write (struct recur_apoint *o, FILE *f)
{
struct tm *lt;
@@ -492,7 +492,7 @@ recur_apoint_write (struct recur_apoint *o, FILE *f)
}
/* Writting of a recursive event into file. */
-static void
+void
recur_event_write (struct recur_event *o, FILE *f)
{
struct tm *lt;
diff --git a/src/todo.c b/src/todo.c
index 8000197..c11a676 100644
--- a/src/todo.c
+++ b/src/todo.c
@@ -183,6 +183,15 @@ todo_add (char *mesg, int id, char *note)
return todo;
}
+void
+todo_write (struct todo *todo, FILE *f)
+{
+ if (todo->note)
+ (void)fprintf (f, "[%d]>%s %s\n", todo->id, todo->note, todo->mesg);
+ else
+ (void)fprintf (f, "[%d] %s\n", todo->id, todo->mesg);
+}
+
/* Delete a note previously attached to a todo item. */
static void
todo_delete_note_bynum (unsigned num)
@@ -369,12 +378,13 @@ todo_edit_item (void)
/* Display todo items in the corresponding panel. */
static void
-display_todo_item (int incolor, char *msg, int prio, int note, int len, int y,
+display_todo_item (int incolor, char *msg, int prio, int note, int width, int y,
int x)
{
WINDOW *w;
int ch_note;
- char buf[len], priostr[2];
+ char buf[width * UTF8_MAXLEN], priostr[2];
+ int i;
w = win[TOD].p;
ch_note = (note) ? '>' : '.';
@@ -385,12 +395,20 @@ display_todo_item (int incolor, char *msg, int prio, int note, int len, int y,
if (incolor == 0)
custom_apply_attr (w, ATTR_HIGHEST);
- if (strlen (msg) < len)
+ if (utf8_strwidth (msg) < width)
mvwprintw (w, y, x, "%s%c %s", priostr, ch_note, msg);
else
{
- (void)strncpy (buf, msg, len - 1);
- buf[len - 1] = '\0';
+ for (i = 0; msg[i] && width > 0; i++)
+ {
+ if (!UTF8_ISCONT (msg[i]))
+ width -= utf8_width (&msg[i]);
+ buf[i] = msg[i];
+ }
+ if (i)
+ buf[i - 1] = 0;
+ else
+ buf[0] = 0;
mvwprintw (w, y, x, "%s%c %s...", priostr, ch_note, buf);
}
if (incolor == 0)
@@ -486,6 +504,35 @@ todo_view_note (char *pager)
wins_launch_external (fullname, pager);
}
+/* Pipe a todo item to an external program. */
+void
+todo_pipe_item (void)
+{
+ char cmd[BUFSIZ] = "";
+ int pout;
+ int pid;
+ FILE *fpout;
+ struct todo *todo;
+
+ status_mesg (_("Pipe item to external command:"), "");
+ if (getstring (win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
+ return;
+
+ wins_prepare_external ();
+ if ((pid = shell_exec (NULL, &pout, cmd)))
+ {
+ fpout = fdopen (pout, "w");
+
+ todo = todo_get_item (hilt);
+ todo_write (todo, fpout);
+
+ fclose (fpout);
+ child_wait (NULL, &pout, pid);
+ press_any_key ();
+ }
+ wins_unprepare_external ();
+}
+
void
todo_free (struct todo *todo)
{
diff --git a/src/utf8.c b/src/utf8.c
new file mode 100644
index 0000000..1bb199c
--- /dev/null
+++ b/src/utf8.c
@@ -0,0 +1,344 @@
+/*
+ * Calcurse - text-based organizer
+ *
+ * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Send your feedback or comments to : misc@calcurse.org
+ * Calcurse home page : http://calcurse.org
+ *
+ */
+
+#include "calcurse.h"
+
+struct utf8_range {
+ int min, max, width;
+};
+
+static const struct utf8_range utf8_widthtab[] = {
+ { 0x00300, 0x0036f, 0 },
+ { 0x00483, 0x00489, 0 },
+ { 0x00591, 0x005bd, 0 },
+ { 0x005bf, 0x005bf, 0 },
+ { 0x005c1, 0x005c2, 0 },
+ { 0x005c4, 0x005c5, 0 },
+ { 0x005c7, 0x005c7, 0 },
+ { 0x00610, 0x0061a, 0 },
+ { 0x0064b, 0x0065e, 0 },
+ { 0x00670, 0x00670, 0 },
+ { 0x006d6, 0x006dc, 0 },
+ { 0x006de, 0x006e4, 0 },
+ { 0x006e7, 0x006e8, 0 },
+ { 0x006ea, 0x006ed, 0 },
+ { 0x00711, 0x00711, 0 },
+ { 0x00730, 0x0074a, 0 },
+ { 0x007a6, 0x007b0, 0 },
+ { 0x007eb, 0x007f3, 0 },
+ { 0x00816, 0x00819, 0 },
+ { 0x0081b, 0x00823, 0 },
+ { 0x00825, 0x00827, 0 },
+ { 0x00829, 0x0082d, 0 },
+ { 0x00900, 0x00903, 0 },
+ { 0x0093c, 0x0093c, 0 },
+ { 0x0093e, 0x0094e, 0 },
+ { 0x00951, 0x00955, 0 },
+ { 0x00962, 0x00963, 0 },
+ { 0x00981, 0x00983, 0 },
+ { 0x009bc, 0x009bc, 0 },
+ { 0x009be, 0x009c4, 0 },
+ { 0x009c7, 0x009c8, 0 },
+ { 0x009cb, 0x009cd, 0 },
+ { 0x009d7, 0x009d7, 0 },
+ { 0x009e2, 0x009e3, 0 },
+ { 0x00a01, 0x00a03, 0 },
+ { 0x00a3c, 0x00a3c, 0 },
+ { 0x00a3e, 0x00a42, 0 },
+ { 0x00a47, 0x00a48, 0 },
+ { 0x00a4b, 0x00a4d, 0 },
+ { 0x00a51, 0x00a51, 0 },
+ { 0x00a70, 0x00a71, 0 },
+ { 0x00a75, 0x00a75, 0 },
+ { 0x00a81, 0x00a83, 0 },
+ { 0x00abc, 0x00abc, 0 },
+ { 0x00abe, 0x00ac5, 0 },
+ { 0x00ac7, 0x00ac9, 0 },
+ { 0x00acb, 0x00acd, 0 },
+ { 0x00ae2, 0x00ae3, 0 },
+ { 0x00b01, 0x00b03, 0 },
+ { 0x00b3c, 0x00b3c, 0 },
+ { 0x00b3e, 0x00b44, 0 },
+ { 0x00b47, 0x00b48, 0 },
+ { 0x00b4b, 0x00b4d, 0 },
+ { 0x00b56, 0x00b57, 0 },
+ { 0x00b62, 0x00b63, 0 },
+ { 0x00b82, 0x00b82, 0 },
+ { 0x00bbe, 0x00bc2, 0 },
+ { 0x00bc6, 0x00bc8, 0 },
+ { 0x00bca, 0x00bcd, 0 },
+ { 0x00bd7, 0x00bd7, 0 },
+ { 0x00c01, 0x00c03, 0 },
+ { 0x00c3e, 0x00c44, 0 },
+ { 0x00c46, 0x00c48, 0 },
+ { 0x00c4a, 0x00c4d, 0 },
+ { 0x00c55, 0x00c56, 0 },
+ { 0x00c62, 0x00c63, 0 },
+ { 0x00c82, 0x00c83, 0 },
+ { 0x00cbc, 0x00cbc, 0 },
+ { 0x00cbe, 0x00cc4, 0 },
+ { 0x00cc6, 0x00cc8, 0 },
+ { 0x00cca, 0x00ccd, 0 },
+ { 0x00cd5, 0x00cd6, 0 },
+ { 0x00ce2, 0x00ce3, 0 },
+ { 0x00d02, 0x00d03, 0 },
+ { 0x00d3e, 0x00d44, 0 },
+ { 0x00d46, 0x00d48, 0 },
+ { 0x00d4a, 0x00d4d, 0 },
+ { 0x00d57, 0x00d57, 0 },
+ { 0x00d62, 0x00d63, 0 },
+ { 0x00d82, 0x00d83, 0 },
+ { 0x00dca, 0x00dca, 0 },
+ { 0x00dcf, 0x00dd4, 0 },
+ { 0x00dd6, 0x00dd6, 0 },
+ { 0x00dd8, 0x00ddf, 0 },
+ { 0x00df2, 0x00df3, 0 },
+ { 0x00e31, 0x00e31, 0 },
+ { 0x00e34, 0x00e3a, 0 },
+ { 0x00e47, 0x00e4e, 0 },
+ { 0x00eb1, 0x00eb1, 0 },
+ { 0x00eb4, 0x00eb9, 0 },
+ { 0x00ebb, 0x00ebc, 0 },
+ { 0x00ec8, 0x00ecd, 0 },
+ { 0x00f18, 0x00f19, 0 },
+ { 0x00f35, 0x00f35, 0 },
+ { 0x00f37, 0x00f37, 0 },
+ { 0x00f39, 0x00f39, 0 },
+ { 0x00f3e, 0x00f3f, 0 },
+ { 0x00f71, 0x00f84, 0 },
+ { 0x00f86, 0x00f87, 0 },
+ { 0x00f90, 0x00f97, 0 },
+ { 0x00f99, 0x00fbc, 0 },
+ { 0x00fc6, 0x00fc6, 0 },
+ { 0x0102b, 0x0103e, 0 },
+ { 0x01056, 0x01059, 0 },
+ { 0x0105e, 0x01060, 0 },
+ { 0x01062, 0x01064, 0 },
+ { 0x01067, 0x0106d, 0 },
+ { 0x01071, 0x01074, 0 },
+ { 0x01082, 0x0108d, 0 },
+ { 0x0108f, 0x0108f, 0 },
+ { 0x0109a, 0x0109d, 0 },
+ { 0x01100, 0x0115f, 2 },
+ { 0x011a3, 0x011a7, 2 },
+ { 0x011fa, 0x011ff, 2 },
+ { 0x0135f, 0x0135f, 0 },
+ { 0x01712, 0x01714, 0 },
+ { 0x01732, 0x01734, 0 },
+ { 0x01752, 0x01753, 0 },
+ { 0x01772, 0x01773, 0 },
+ { 0x017b6, 0x017d3, 0 },
+ { 0x017dd, 0x017dd, 0 },
+ { 0x0180b, 0x0180d, 0 },
+ { 0x018a9, 0x018a9, 0 },
+ { 0x01920, 0x0192b, 0 },
+ { 0x01930, 0x0193b, 0 },
+ { 0x019b0, 0x019c0, 0 },
+ { 0x019c8, 0x019c9, 0 },
+ { 0x01a17, 0x01a1b, 0 },
+ { 0x01a55, 0x01a5e, 0 },
+ { 0x01a60, 0x01a7c, 0 },
+ { 0x01a7f, 0x01a7f, 0 },
+ { 0x01b00, 0x01b04, 0 },
+ { 0x01b34, 0x01b44, 0 },
+ { 0x01b6b, 0x01b73, 0 },
+ { 0x01b80, 0x01b82, 0 },
+ { 0x01ba1, 0x01baa, 0 },
+ { 0x01c24, 0x01c37, 0 },
+ { 0x01cd0, 0x01cd2, 0 },
+ { 0x01cd4, 0x01ce8, 0 },
+ { 0x01ced, 0x01ced, 0 },
+ { 0x01cf2, 0x01cf2, 0 },
+ { 0x01dc0, 0x01de6, 0 },
+ { 0x01dfd, 0x01dff, 0 },
+ { 0x020d0, 0x020f0, 0 },
+ { 0x02329, 0x0232a, 2 },
+ { 0x02cef, 0x02cf1, 0 },
+ { 0x02de0, 0x02dff, 0 },
+ { 0x02e80, 0x02e99, 2 },
+ { 0x02e9b, 0x02ef3, 2 },
+ { 0x02f00, 0x02fd5, 2 },
+ { 0x02ff0, 0x02ffb, 2 },
+ { 0x03000, 0x03029, 2 },
+ { 0x0302a, 0x0302f, 0 },
+ { 0x03030, 0x0303e, 2 },
+ { 0x03041, 0x03096, 2 },
+ { 0x03099, 0x0309a, 0 },
+ { 0x0309b, 0x030ff, 2 },
+ { 0x03105, 0x0312d, 2 },
+ { 0x03131, 0x0318e, 2 },
+ { 0x03190, 0x031b7, 2 },
+ { 0x031c0, 0x031e3, 2 },
+ { 0x031f0, 0x0321e, 2 },
+ { 0x03220, 0x03247, 2 },
+ { 0x03250, 0x032fe, 2 },
+ { 0x03300, 0x04dbf, 2 },
+ { 0x04e00, 0x0a48c, 2 },
+ { 0x0a490, 0x0a4c6, 2 },
+ { 0x0a66f, 0x0a672, 0 },
+ { 0x0a67c, 0x0a67d, 0 },
+ { 0x0a6f0, 0x0a6f1, 0 },
+ { 0x0a802, 0x0a802, 0 },
+ { 0x0a806, 0x0a806, 0 },
+ { 0x0a80b, 0x0a80b, 0 },
+ { 0x0a823, 0x0a827, 0 },
+ { 0x0a880, 0x0a881, 0 },
+ { 0x0a8b4, 0x0a8c4, 0 },
+ { 0x0a8e0, 0x0a8f1, 0 },
+ { 0x0a926, 0x0a92d, 0 },
+ { 0x0a947, 0x0a953, 0 },
+ { 0x0a960, 0x0a97c, 2 },
+ { 0x0a980, 0x0a983, 0 },
+ { 0x0a9b3, 0x0a9c0, 0 },
+ { 0x0aa29, 0x0aa36, 0 },
+ { 0x0aa43, 0x0aa43, 0 },
+ { 0x0aa4c, 0x0aa4d, 0 },
+ { 0x0aa7b, 0x0aa7b, 0 },
+ { 0x0aab0, 0x0aab0, 0 },
+ { 0x0aab2, 0x0aab4, 0 },
+ { 0x0aab7, 0x0aab8, 0 },
+ { 0x0aabe, 0x0aabf, 0 },
+ { 0x0aac1, 0x0aac1, 0 },
+ { 0x0abe3, 0x0abea, 0 },
+ { 0x0abec, 0x0abed, 0 },
+ { 0x0ac00, 0x0d7a3, 2 },
+ { 0x0d7b0, 0x0d7c6, 2 },
+ { 0x0d7cb, 0x0d7fb, 2 },
+ { 0x0f900, 0x0faff, 2 },
+ { 0x0fb1e, 0x0fb1e, 0 },
+ { 0x0fe00, 0x0fe0f, 0 },
+ { 0x0fe10, 0x0fe19, 2 },
+ { 0x0fe20, 0x0fe26, 0 },
+ { 0x0fe30, 0x0fe52, 2 },
+ { 0x0fe54, 0x0fe66, 2 },
+ { 0x0fe68, 0x0fe6b, 2 },
+ { 0x0ff01, 0x0ff60, 2 },
+ { 0x0ffe0, 0x0ffe6, 2 },
+ { 0x101fd, 0x101fd, 0 },
+ { 0x10a01, 0x10a03, 0 },
+ { 0x10a05, 0x10a06, 0 },
+ { 0x10a0c, 0x10a0f, 0 },
+ { 0x10a38, 0x10a3a, 0 },
+ { 0x10a3f, 0x10a3f, 0 },
+ { 0x11080, 0x11082, 0 },
+ { 0x110b0, 0x110ba, 0 },
+ { 0x1d165, 0x1d169, 0 },
+ { 0x1d16d, 0x1d172, 0 },
+ { 0x1d17b, 0x1d182, 0 },
+ { 0x1d185, 0x1d18b, 0 },
+ { 0x1d1aa, 0x1d1ad, 0 },
+ { 0x1d242, 0x1d244, 0 },
+ { 0x1f200, 0x1f200, 2 },
+ { 0x1f210, 0x1f231, 2 },
+ { 0x1f240, 0x1f248, 2 },
+ { 0x20000, 0x2fffd, 2 },
+ { 0x30000, 0x3fffd, 2 },
+ { 0xe0100, 0xe01ef, 0 }
+};
+
+/* Get the width of a UTF-8 character. */
+int
+utf8_width (char *s)
+{
+ int val, low, high, cur;
+
+ if (UTF8_ISCONT (*s))
+ return 0;
+
+ switch (UTF8_LENGTH (*s))
+ {
+ case 1:
+ val = s[0];
+ break;
+ case 2:
+ val = (s[1] & 0x3f) | (s[0] & 0x1f) << 6;
+ break;
+ case 3:
+ val = ((s[2] & 0x3f) | (s[1] & 0x3f) << 6) |
+ (s[0] & 0x0f) << 12;
+ break;
+ case 4:
+ val = (((s[3] & 0x3f) | (s[2] & 0x3f) << 6) |
+ (s[1] & 0x3f) << 12) | (s[0] & 0x3f) << 18;
+ break;
+ case 5:
+ val = ((((s[4] & 0x3f) | (s[3] & 0x3f) << 6) |
+ (s[2] & 0x3f) << 12) | (s[1] & 0x3f) << 18) |
+ (s[0] & 0x3f) << 24;
+ break;
+ case 6:
+ val = (((((s[5] & 0x3f) | (s[4] & 0x3f) << 6) |
+ (s[3] & 0x3f) << 12) | (s[2] & 0x3f) << 18) |
+ (s[1] & 0x3f) << 24) | (s[0] & 0x3f) << 30;
+ break;
+ default:
+ return 0;
+ }
+
+ low = 0;
+ high = sizeof(utf8_widthtab) / sizeof(utf8_widthtab[0]);
+ do
+ {
+ cur = (low + high) / 2;
+ if (val >= utf8_widthtab[cur].min)
+ {
+ if (val <= utf8_widthtab[cur].max)
+ return utf8_widthtab[cur].width;
+ else
+ low = cur + 1;
+ }
+ else
+ high = cur - 1;
+ }
+ while (low <= high);
+
+ return 1;
+}
+
+/* Get the width of a UTF-8 string. */
+int
+utf8_strwidth (char *s)
+{
+ int width = 0;
+
+ for (; s && *s; s++)
+ {
+ if (!UTF8_ISCONT (*s))
+ width += utf8_width (s);
+ }
+
+ return width;
+}
diff --git a/src/utils.c b/src/utils.c
index b165111..949b1b7 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"
@@ -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)
@@ -916,3 +735,108 @@ 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);
+}
diff --git a/src/wins.c b/src/wins.c
index 7b7384b..0e4b402 100644
--- a/src/wins.c
+++ b/src/wins.c
@@ -599,33 +599,23 @@ wins_reset (void)
wins_update ();
}
-/*
- * While inside interactive mode, launch the external command cmd on the given
- * file.
- */
+/* Prepare windows for the execution of an external command. */
void
-wins_launch_external (const char *file, const char *cmd)
+wins_prepare_external (void)
{
- char *p;
- int len;
-
- /* Beware of space between cmd and file. */
- len = strlen (file) + strlen (cmd) + 2;
-
- p = (char *) mem_calloc (len, sizeof (char));
- if (snprintf (p, len, "%s %s", cmd, file) == -1)
- {
- mem_free (p);
- return;
- }
if (notify_bar ())
notify_stop_main_thread ();
def_prog_mode ();
- endwin ();
ui_mode = UI_CMDLINE;
clear ();
wins_refresh ();
- (void)system (p);
+ endwin ();
+}
+
+/* Restore windows when returning from an external command. */
+void
+wins_unprepare_external (void)
+{
reset_prog_mode ();
clearok (curscr, TRUE);
curs_set (0);
@@ -633,12 +623,27 @@ wins_launch_external (const char *file, const char *cmd)
wins_refresh ();
if (notify_bar ())
notify_start_main_thread ();
- mem_free (p);
+}
+
+/*
+ * While inside interactive mode, launch the external command cmd on the given
+ * file.
+ */
+void
+wins_launch_external (char *file, char *cmd)
+{
+ char *arg[] = { cmd, file, NULL };
+ int pid;
+
+ wins_prepare_external ();
+ if ((pid = fork_exec (NULL, NULL, cmd, arg)))
+ child_wait (NULL, NULL, pid);
+ wins_unprepare_external ();
}
#define NB_CAL_CMDS 27 /* number of commands while in cal view */
-#define NB_APP_CMDS 31 /* same thing while in appointment view */
-#define NB_TOD_CMDS 30 /* same thing while in todo view */
+#define NB_APP_CMDS 32 /* same thing while in appointment view */
+#define NB_TOD_CMDS 31 /* same thing while in todo view */
#define TOTAL_CMDS NB_CAL_CMDS + NB_APP_CMDS + NB_TOD_CMDS
#define CMDS_PER_LINE 6 /* max number of commands per line */
@@ -690,6 +695,7 @@ wins_status_bar (void)
struct binding del = {_("Del Item"), KEY_DEL_ITEM};
struct binding edit = {_("Edit Itm"), KEY_EDIT_ITEM};
struct binding view = {_("View"), KEY_VIEW_ITEM};
+ struct binding pipe = {_("Pipe"), KEY_PIPE_ITEM};
struct binding flag = {_("Flag Itm"), KEY_FLAG_ITEM};
struct binding rept = {_("Repeat"), KEY_REPEAT_ITEM};
struct binding enote = {_("EditNote"), KEY_EDIT_NOTE};
@@ -704,13 +710,14 @@ wins_status_bar (void)
&gnday, &gpday, &gnweek, &gpweek, &draw, &othr, &today, &conf, &othr,
/* appointment keys */
&help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view,
- &draw, &othr, &rept, &flag, &enote, &vnote, &up, &down, &gnday, &gpday,
- &gnweek, &gpweek, &togo, &othr, &today, &conf, &appt, &todo, &cut, &paste,
- &othr,
+ &pipe, &othr, &draw, &rept, &flag, &enote, &vnote, &up, &down, &gnday,
+ &gpday, &gnweek, &gpweek, &othr, &togo, &today, &conf, &appt, &todo, &cut,
+ &paste, &othr,
/* todo keys */
&help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view,
- &flag, &othr, &rprio, &lprio, &enote, &vnote, &up, &down, &gnday, &gpday,
- &gnweek, &gpweek, &togo, &othr, &today, &conf, &appt, &todo, &draw, &othr
+ &pipe, &othr, &flag, &rprio, &lprio, &enote, &vnote, &up, &down, &gnday,
+ &gpday, &gnweek, &gpweek, &othr, &togo, &today, &conf, &appt, &todo, &draw,
+ &othr
};
/* Drawing the keybinding with attribute and label without. */