2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
12 #include <lib-sys/unix.h>
14 #include <lib-ui/menu.h>
15 #include <lib-ui/curses.h>
19 #include "mutt_idna.h"
22 typedef struct query {
29 typedef struct entry {
34 /* Variables for outsizing output format */
35 static int FirstColumn;
36 static int SecondColumn;
38 static void query_menu (char *buf, ssize_t buflen, QUERY * results,
41 static address_t *result_to_addr (QUERY * r)
43 static address_t *tmp;
45 tmp = address_list_dup (r->addr);
47 if (!tmp->next && !tmp->personal)
48 tmp->personal = m_strdup(r->name);
50 mutt_addrlist_to_idna (tmp, NULL);
54 static QUERY *run_query (char *s, int quiet)
59 char cmd[_POSIX_PATH_MAX];
69 m_quotefile_fmt(cmd, sizeof (cmd), QueryCmd, s);
71 if ((thepid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
75 mutt_message _("Waiting for response...");
77 fgets (msg, sizeof (msg), fp);
78 if ((p = strrchr (msg, '\n')))
80 while ((buf = mutt_read_line (buf, &buflen, fp, &dummy)) != NULL) {
81 if ((p = strtok (buf, "\t\n"))) {
85 first = p_new(QUERY, 1);
89 cur->next = p_new(QUERY, 1);
97 cur->addr = rfc822_parse_adrlist (cur->addr, p);
98 p = strtok (NULL, "\t\n");
103 cur->name = m_strdup(p);
104 p = strtok (NULL, "\t\n");
106 cur->other = m_strdup(p);
113 if (mutt_wait_filter (thepid)) {
115 mutt_error ("%s", msg);
119 mutt_message ("%s", msg);
125 static int query_search (MUTTMENU * m, regex_t * re, int n)
127 ENTRY *table = (ENTRY *) m->data;
129 if (table[n].data->name && !regexec (re, table[n].data->name, 0, NULL, 0))
131 if (table[n].data->other && !regexec (re, table[n].data->other, 0, NULL, 0))
133 if (table[n].data->addr) {
134 if (table[n].data->addr->personal &&
135 !regexec (re, table[n].data->addr->personal, 0, NULL, 0))
137 if (table[n].data->addr->mailbox &&
138 !regexec (re, table[n].data->addr->mailbox, 0, NULL, 0))
145 /* This is the callback routine from mutt_menuLoop() which is used to generate
146 * a menu entry for the requested item number.
148 #define QUERY_MIN_COLUMN_LENGHT 20 /* Must be < 70/2 */
149 static void query_entry (char *s, ssize_t slen, MUTTMENU * m, int num)
151 ENTRY *table = (ENTRY *) m->data;
152 char buf2[STRING], buf[STRING] = "";
154 /* need a query format ... hard coded constants are not good */
155 while (FirstColumn + SecondColumn > 70) {
156 FirstColumn = FirstColumn * 3 / 4;
157 SecondColumn = SecondColumn * 3 / 4;
158 if (FirstColumn < QUERY_MIN_COLUMN_LENGHT)
159 FirstColumn = QUERY_MIN_COLUMN_LENGHT;
160 if (SecondColumn < QUERY_MIN_COLUMN_LENGHT)
161 SecondColumn = QUERY_MIN_COLUMN_LENGHT;
164 rfc822_addrcat(buf, sizeof (buf), table[num].data->addr, 1);
166 mutt_format_string (buf2, sizeof (buf2),
167 FirstColumn + 2, FirstColumn + 2,
168 0, ' ', table[num].data->name,
169 m_strlen(table[num].data->name), 0);
171 snprintf (s, slen, " %c %3d %s %-*.*s %s",
172 table[num].tagged ? '*' : ' ',
176 SecondColumn + 2, buf, NONULL (table[num].data->other));
179 static int query_tag (MUTTMENU * menu, int n, int m)
181 ENTRY *cur = &((ENTRY *) menu->data)[n];
182 int ot = cur->tagged;
184 cur->tagged = m >= 0 ? m : !cur->tagged;
185 return cur->tagged - ot;
188 int mutt_query_complete (char *buf, ssize_t buflen)
190 QUERY *results = NULL;
194 mutt_error _("Query command not defined.");
199 results = run_query (buf, 1);
201 /* only one response? */
202 if (results->next == NULL) {
203 tmpa = result_to_addr (results);
204 mutt_addrlist_to_local (tmpa);
206 rfc822_addrcat(buf, buflen, tmpa, 0);
207 address_list_wipe(&tmpa);
211 /* multiple results, choose from query menu */
212 query_menu (buf, buflen, results, 1);
217 void mutt_query_menu (char *buf, ssize_t buflen)
220 mutt_error _("Query command not defined.");
226 char buffer[STRING] = "";
228 query_menu (buffer, sizeof (buffer), NULL, 0);
231 query_menu (buf, buflen, NULL, 1);
235 static void query_menu (char *buf, ssize_t buflen, QUERY * results, int retbuf)
239 ENTRY *QueryTable = NULL;
240 QUERY *queryp = NULL;
245 snprintf (title, sizeof (title), _("Query")); /* FIXME */
247 menu = mutt_new_menu ();
248 menu->make_entry = query_entry;
249 menu->search = query_search;
250 menu->tag = query_tag;
251 menu->menu = MENU_QUERY;
254 if (results == NULL) {
255 /* Prompt for Query */
256 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) {
257 results = run_query (buf, 0);
262 snprintf (title, sizeof (title), _("Query '%s'"), buf);
264 /* count the number of results */
265 for (queryp = results; queryp; queryp = queryp->next)
268 menu->data = QueryTable = p_new(ENTRY, menu->max);
270 for (i = 0, queryp = results; queryp; queryp = queryp->next, i++)
271 QueryTable[i].data = queryp;
274 switch ((op = mutt_menuLoop (menu))) {
275 case OP_QUERY_APPEND:
277 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) {
278 QUERY *newresults = NULL;
280 newresults = run_query (buf, 0);
282 menu->redraw = REDRAW_FULL;
284 snprintf (title, sizeof (title), _("Query '%s'"), buf);
286 if (op == OP_QUERY) {
289 address_list_wipe(&queryp->addr);
290 p_delete(&queryp->name);
291 p_delete(&queryp->other);
292 results = queryp->next;
296 results = newresults;
297 p_delete(&QueryTable);
301 for (queryp = results; queryp->next; queryp = queryp->next);
303 queryp->next = newresults;
308 mutt_menuDestroy (&menu);
309 menu = mutt_new_menu ();
310 menu->make_entry = query_entry;
311 menu->search = query_search;
312 menu->tag = query_tag;
313 menu->menu = MENU_QUERY;
316 /* count the number of results */
317 for (queryp = results; queryp; queryp = queryp->next)
320 if (op == OP_QUERY) {
321 menu->data = QueryTable = p_new(ENTRY, menu->max);
323 for (i = 0, queryp = results; queryp;
324 queryp = queryp->next, i++)
325 QueryTable[i].data = queryp;
331 p_realloc(&QueryTable, menu->max);
333 menu->data = QueryTable;
335 for (i = 0, queryp = results; queryp;
336 queryp = queryp->next, i++) {
337 /* once we hit new entries, clear/init the tag */
338 if (queryp == newresults)
341 QueryTable[i].data = queryp;
343 QueryTable[i].tagged = 0;
350 case OP_CREATE_ALIAS:
351 if (menu->tagprefix) {
352 address_t *naddr = NULL;
354 for (i = 0; i < menu->max; i++)
355 if (QueryTable[i].tagged) {
356 address_list_append(&naddr, result_to_addr(QueryTable[i].data));
359 mutt_create_alias (NULL, naddr);
362 address_t *a = result_to_addr (QueryTable[menu->current].data);
364 mutt_create_alias (NULL, a);
365 address_list_wipe(&a);
369 case OP_GENERIC_SELECT_ENTRY:
374 /* fall through to OP_MAIL */
378 msg->env = envelope_new();
379 if (!menu->tagprefix) {
380 msg->env->to = result_to_addr (QueryTable[menu->current].data);
383 for (i = 0; i < menu->max; i++)
384 if (QueryTable[i].tagged) {
385 address_list_append(&msg->env->to, result_to_addr(QueryTable[i].data));
388 ci_send_message (0, msg, NULL, Context, NULL);
389 menu->redraw = REDRAW_FULL;
398 /* if we need to return the selected entries */
399 if (retbuf && (done == 2)) {
403 p_clear(buf, buflen);
405 /* check for tagged entries */
406 for (i = 0; i < menu->max; i++) {
407 if (QueryTable[i].tagged) {
409 address_t *tmpa = result_to_addr (QueryTable[i].data);
411 mutt_addrlist_to_local (tmpa);
413 rfc822_addrcat(buf, buflen, tmpa, 0);
414 curpos = m_strlen(buf);
415 address_list_wipe(&tmpa);
417 else if (curpos + 2 < buflen) {
418 address_t *tmpa = result_to_addr (QueryTable[i].data);
420 mutt_addrlist_to_local (tmpa);
421 m_strcat(buf, buflen, ", ");
422 rfc822_addrcat(buf + curpos + 1, buflen - curpos - 1, tmpa, 0);
423 curpos = m_strlen(buf);
424 address_list_wipe(&tmpa);
428 /* then enter current message */
430 address_t *tmpa = result_to_addr (QueryTable[menu->current].data);
432 mutt_addrlist_to_local (tmpa);
433 rfc822_addrcat(buf, buflen, tmpa, 0);
434 address_list_wipe(&tmpa);
441 address_list_wipe(&queryp->addr);
442 p_delete(&queryp->name);
443 p_delete(&queryp->other);
444 results = queryp->next;
448 p_delete(&QueryTable);
450 /* tell whoever called me to redraw the screen when I return */
451 set_option (OPTNEEDREDRAW);
454 mutt_menuDestroy (&menu);