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.
18 #include <lib-lib/mem.h>
19 #include <lib-lib/str.h>
20 #include <lib-lib/macros.h>
21 #include <lib-lib/file.h>
22 #include <lib-lib/mapping.h>
23 #include <lib-lib/debug.h>
25 #include <lib-ui/menu.h>
29 #include "mutt_idna.h"
32 typedef struct query {
39 typedef struct entry {
44 static struct mapping_t QueryHelp[] = {
45 {N_("Exit"), OP_EXIT},
46 {N_("Mail"), OP_MAIL},
47 {N_("New Query"), OP_QUERY},
48 {N_("Make Alias"), OP_CREATE_ALIAS},
49 {N_("Search"), OP_SEARCH},
50 {N_("Help"), OP_HELP},
54 /* Variables for outsizing output format */
55 static int FirstColumn;
56 static int SecondColumn;
58 static void query_menu (char *buf, ssize_t buflen, QUERY * results,
61 static address_t *result_to_addr (QUERY * r)
63 static address_t *tmp;
65 tmp = address_list_dup (r->addr);
67 if (!tmp->next && !tmp->personal)
68 tmp->personal = m_strdup(r->name);
70 mutt_addrlist_to_idna (tmp, NULL);
74 static QUERY *run_query (char *s, int quiet)
79 char cmd[_POSIX_PATH_MAX];
89 mutt_expand_file_fmt (cmd, sizeof (cmd), QueryCmd, s);
91 if ((thepid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
92 debug_print (1, ("unable to fork command: %s\n", cmd));
96 mutt_message _("Waiting for response...");
98 fgets (msg, sizeof (msg), fp);
99 if ((p = strrchr (msg, '\n')))
101 while ((buf = mutt_read_line (buf, &buflen, fp, &dummy)) != NULL) {
102 if ((p = strtok (buf, "\t\n"))) {
106 first = p_new(QUERY, 1);
110 cur->next = p_new(QUERY, 1);
114 l = mutt_strwidth (p);
115 if (l > SecondColumn)
118 cur->addr = rfc822_parse_adrlist (cur->addr, p);
119 p = strtok (NULL, "\t\n");
121 l = mutt_strwidth (p);
124 cur->name = m_strdup(p);
125 p = strtok (NULL, "\t\n");
127 cur->other = m_strdup(p);
134 if (mutt_wait_filter (thepid)) {
135 debug_print (1, ("Error: %s\n", msg));
137 mutt_error ("%s", msg);
141 mutt_message ("%s", msg);
147 static int query_search (MUTTMENU * m, regex_t * re, int n)
149 ENTRY *table = (ENTRY *) m->data;
151 if (table[n].data->name && !regexec (re, table[n].data->name, 0, NULL, 0))
153 if (table[n].data->other && !regexec (re, table[n].data->other, 0, NULL, 0))
155 if (table[n].data->addr) {
156 if (table[n].data->addr->personal &&
157 !regexec (re, table[n].data->addr->personal, 0, NULL, 0))
159 if (table[n].data->addr->mailbox &&
160 !regexec (re, table[n].data->addr->mailbox, 0, NULL, 0))
167 /* This is the callback routine from mutt_menuLoop() which is used to generate
168 * a menu entry for the requested item number.
170 #define QUERY_MIN_COLUMN_LENGHT 20 /* Must be < 70/2 */
171 static void query_entry (char *s, ssize_t slen, MUTTMENU * m, int num)
173 ENTRY *table = (ENTRY *) m->data;
174 char buf2[SHORT_STRING], buf[SHORT_STRING] = "";
176 /* need a query format ... hard coded constants are not good */
177 while (FirstColumn + SecondColumn > 70) {
178 FirstColumn = FirstColumn * 3 / 4;
179 SecondColumn = SecondColumn * 3 / 4;
180 if (FirstColumn < QUERY_MIN_COLUMN_LENGHT)
181 FirstColumn = QUERY_MIN_COLUMN_LENGHT;
182 if (SecondColumn < QUERY_MIN_COLUMN_LENGHT)
183 SecondColumn = QUERY_MIN_COLUMN_LENGHT;
186 rfc822_write_address (buf, sizeof (buf), table[num].data->addr, 1);
188 mutt_format_string (buf2, sizeof (buf2),
189 FirstColumn + 2, FirstColumn + 2,
190 0, ' ', table[num].data->name,
191 m_strlen(table[num].data->name), 0);
193 snprintf (s, slen, " %c %3d %s %-*.*s %s",
194 table[num].tagged ? '*' : ' ',
198 SecondColumn + 2, buf, NONULL (table[num].data->other));
201 static int query_tag (MUTTMENU * menu, int n, int m)
203 ENTRY *cur = &((ENTRY *) menu->data)[n];
204 int ot = cur->tagged;
206 cur->tagged = m >= 0 ? m : !cur->tagged;
207 return cur->tagged - ot;
210 int mutt_query_complete (char *buf, ssize_t buflen)
212 QUERY *results = NULL;
216 mutt_error _("Query command not defined.");
221 results = run_query (buf, 1);
223 /* only one response? */
224 if (results->next == NULL) {
225 tmpa = result_to_addr (results);
226 mutt_addrlist_to_local (tmpa);
228 rfc822_write_address (buf, buflen, tmpa, 0);
229 address_list_wipe(&tmpa);
233 /* multiple results, choose from query menu */
234 query_menu (buf, buflen, results, 1);
239 void mutt_query_menu (char *buf, ssize_t buflen)
242 mutt_error _("Query command not defined.");
248 char buffer[STRING] = "";
250 query_menu (buffer, sizeof (buffer), NULL, 0);
253 query_menu (buf, buflen, NULL, 1);
257 static void query_menu (char *buf, ssize_t buflen, QUERY * results, int retbuf)
261 ENTRY *QueryTable = NULL;
262 QUERY *queryp = NULL;
265 char helpstr[SHORT_STRING];
268 snprintf (title, sizeof (title), _("Query")); /* FIXME */
270 menu = mutt_new_menu ();
271 menu->make_entry = query_entry;
272 menu->search = query_search;
273 menu->tag = query_tag;
274 menu->menu = MENU_QUERY;
277 mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp);
279 if (results == NULL) {
280 /* Prompt for Query */
281 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) {
282 results = run_query (buf, 0);
287 snprintf (title, sizeof (title), _("Query '%s'"), buf);
289 /* count the number of results */
290 for (queryp = results; queryp; queryp = queryp->next)
293 menu->data = QueryTable = p_new(ENTRY, menu->max);
295 for (i = 0, queryp = results; queryp; queryp = queryp->next, i++)
296 QueryTable[i].data = queryp;
299 switch ((op = mutt_menuLoop (menu))) {
300 case OP_QUERY_APPEND:
302 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) {
303 QUERY *newresults = NULL;
305 newresults = run_query (buf, 0);
307 menu->redraw = REDRAW_FULL;
309 snprintf (title, sizeof (title), _("Query '%s'"), buf);
311 if (op == OP_QUERY) {
314 address_list_wipe(&queryp->addr);
315 p_delete(&queryp->name);
316 p_delete(&queryp->other);
317 results = queryp->next;
321 results = newresults;
322 p_delete(&QueryTable);
326 for (queryp = results; queryp->next; queryp = queryp->next);
328 queryp->next = newresults;
333 mutt_menuDestroy (&menu);
334 menu = mutt_new_menu ();
335 menu->make_entry = query_entry;
336 menu->search = query_search;
337 menu->tag = query_tag;
338 menu->menu = MENU_QUERY;
341 mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY,
344 /* count the number of results */
345 for (queryp = results; queryp; queryp = queryp->next)
348 if (op == OP_QUERY) {
349 menu->data = QueryTable = p_new(ENTRY, menu->max);
351 for (i = 0, queryp = results; queryp;
352 queryp = queryp->next, i++)
353 QueryTable[i].data = queryp;
359 p_realloc(&QueryTable, menu->max);
361 menu->data = QueryTable;
363 for (i = 0, queryp = results; queryp;
364 queryp = queryp->next, i++) {
365 /* once we hit new entries, clear/init the tag */
366 if (queryp == newresults)
369 QueryTable[i].data = queryp;
371 QueryTable[i].tagged = 0;
378 case OP_CREATE_ALIAS:
379 if (menu->tagprefix) {
380 address_t *naddr = NULL;
382 for (i = 0; i < menu->max; i++)
383 if (QueryTable[i].tagged) {
384 address_list_append(&naddr, result_to_addr(QueryTable[i].data));
387 mutt_create_alias (NULL, naddr);
390 address_t *a = result_to_addr (QueryTable[menu->current].data);
392 mutt_create_alias (NULL, a);
393 address_list_wipe(&a);
397 case OP_GENERIC_SELECT_ENTRY:
402 /* fall through to OP_MAIL */
406 msg->env = envelope_new();
407 if (!menu->tagprefix) {
408 msg->env->to = result_to_addr (QueryTable[menu->current].data);
411 for (i = 0; i < menu->max; i++)
412 if (QueryTable[i].tagged) {
413 address_list_append(&msg->env->to, result_to_addr(QueryTable[i].data));
416 ci_send_message (0, msg, NULL, Context, NULL);
417 menu->redraw = REDRAW_FULL;
426 /* if we need to return the selected entries */
427 if (retbuf && (done == 2)) {
431 p_clear(buf, buflen);
433 /* check for tagged entries */
434 for (i = 0; i < menu->max; i++) {
435 if (QueryTable[i].tagged) {
437 address_t *tmpa = result_to_addr (QueryTable[i].data);
439 mutt_addrlist_to_local (tmpa);
441 rfc822_write_address (buf, buflen, tmpa, 0);
442 curpos = m_strlen(buf);
443 address_list_wipe(&tmpa);
445 else if (curpos + 2 < buflen) {
446 address_t *tmpa = result_to_addr (QueryTable[i].data);
448 mutt_addrlist_to_local (tmpa);
449 strcat (buf, ", "); /* __STRCAT_CHECKED__ */
450 rfc822_write_address ((char *) buf + curpos + 1,
451 buflen - curpos - 1, tmpa, 0);
452 curpos = m_strlen(buf);
453 address_list_wipe(&tmpa);
457 /* then enter current message */
459 address_t *tmpa = result_to_addr (QueryTable[menu->current].data);
461 mutt_addrlist_to_local (tmpa);
462 rfc822_write_address (buf, buflen, tmpa, 0);
463 address_list_wipe(&tmpa);
470 address_list_wipe(&queryp->addr);
471 p_delete(&queryp->name);
472 p_delete(&queryp->other);
473 results = queryp->next;
477 p_delete(&QueryTable);
479 /* tell whoever called me to redraw the screen when I return */
480 set_option (OPTNEEDREDRAW);
483 mutt_menuDestroy (&menu);