]> git.llucax.com Git - software/mutt-debian.git/blob - query.c
adding a description to a patch which was missing it
[software/mutt-debian.git] / query.c
1 /*
2  * Copyright (C) 1996-2000,2003 Michael R. Elkins <me@mutt.org>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */ 
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mutt_menu.h"
25 #include "mutt_idna.h"
26 #include "mapping.h"
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31
32 typedef struct query
33 {
34   int num;
35   ADDRESS *addr;
36   char *name;
37   char *other;
38   struct query *next;
39 } QUERY;
40
41 typedef struct entry
42 {
43   int tagged;
44   QUERY *data;
45 } ENTRY;
46
47 static struct mapping_t QueryHelp[] = {
48   { N_("Exit"),   OP_EXIT },
49   { N_("Mail"),   OP_MAIL },
50   { N_("New Query"),  OP_QUERY },
51   { N_("Make Alias"), OP_CREATE_ALIAS },
52   { N_("Search"), OP_SEARCH },
53   { N_("Help"),   OP_HELP },
54   { NULL,         0 }
55 };
56
57 static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf);
58
59 static ADDRESS *result_to_addr (QUERY *r)
60 {
61   static ADDRESS *tmp;
62   
63   if (!(tmp = rfc822_cpy_adr (r->addr, 0)))
64     return NULL;
65   
66   if(!tmp->next && !tmp->personal)
67     tmp->personal = safe_strdup (r->name);
68   
69   mutt_addrlist_to_idna (tmp, NULL);
70   return tmp;
71 }
72
73 static QUERY *run_query (char *s, int quiet)
74 {
75   FILE *fp;
76   QUERY *first = NULL;
77   QUERY *cur = NULL;
78   char cmd[_POSIX_PATH_MAX];
79   char *buf = NULL;
80   size_t buflen;
81   int dummy = 0;
82   char msg[STRING];
83   char *p;
84   pid_t thepid;
85
86
87   mutt_expand_file_fmt (cmd, sizeof(cmd), QueryCmd, s);
88
89   if ((thepid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
90   {
91     dprint (1, (debugfile, "unable to fork command: %s", cmd));
92     return 0;
93   }
94   if (!quiet)
95     mutt_message _("Waiting for response...");
96   fgets (msg, sizeof (msg), fp);
97   if ((p = strrchr (msg, '\n')))
98     *p = '\0';
99   while ((buf = mutt_read_line (buf, &buflen, fp, &dummy, 0)) != NULL)
100   {
101     if ((p = strtok(buf, "\t\n")))
102     {
103       if (first == NULL)
104       {
105         first = (QUERY *) safe_calloc (1, sizeof (QUERY));
106         cur = first;
107       }
108       else
109       {
110         cur->next = (QUERY *) safe_calloc (1, sizeof (QUERY));
111         cur = cur->next;
112       }
113
114       cur->addr = rfc822_parse_adrlist (cur->addr, p);
115       p = strtok(NULL, "\t\n");
116       if (p)
117       {
118         cur->name = safe_strdup (p);
119         p = strtok(NULL, "\t\n");
120         if (p)
121           cur->other = safe_strdup (p);
122       }
123     }
124   }
125   FREE (&buf);
126   safe_fclose (&fp);
127   if (mutt_wait_filter (thepid))
128   {
129     dprint (1, (debugfile, "Error: %s\n", msg));
130     if (!quiet)  mutt_error ("%s", msg);
131   }
132   else
133   {
134     if (!quiet)
135       mutt_message ("%s", msg);
136   }
137   
138   return first;
139 }
140
141 static int query_search (MUTTMENU *m, regex_t *re, int n)
142 {
143   ENTRY *table = (ENTRY *) m->data;
144
145   if (table[n].data->name && !regexec (re, table[n].data->name, 0, NULL, 0))
146     return 0;
147   if (table[n].data->other && !regexec (re, table[n].data->other, 0, NULL, 0))
148     return 0;
149   if (table[n].data->addr)
150   {
151     if (table[n].data->addr->personal && 
152         !regexec (re, table[n].data->addr->personal, 0, NULL, 0))
153       return 0;
154     if (table[n].data->addr->mailbox &&
155         !regexec (re, table[n].data->addr->mailbox, 0, NULL, 0))
156       return 0;
157 #ifdef EXACT_ADDRESS
158     if (table[n].data->addr->val &&
159         !regexec (re, table[n].data->addr->val, 0, NULL, 0))
160       return 0;
161 #endif
162   }
163   
164   return REG_NOMATCH;
165 }
166
167 static const char * query_format_str (char *dest, size_t destlen, size_t col,
168                                       char op, const char *src,
169                                       const char *fmt, const char *ifstring,
170                                       const char *elsestring,
171                                       unsigned long data, format_flag flags)
172 {
173   ENTRY *entry = (ENTRY *)data;
174   QUERY *query = entry->data;
175   char tmp[SHORT_STRING];
176   char buf2[STRING] = "";
177   int optional = (flags & M_FORMAT_OPTIONAL);
178
179   switch (op)
180   {
181   case 'a':
182     rfc822_write_address (buf2, sizeof (buf2), query->addr, 1);
183     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
184     snprintf (dest, destlen, tmp, buf2);
185     break;
186   case 'c':
187     snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
188     snprintf (dest, destlen, tmp, query->num + 1);
189     break;
190   case 'e':
191     if (!optional)
192     {
193       snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
194       snprintf (dest, destlen, tmp, NONULL (query->other));
195     }
196     else if (!query->other || !*query->other)
197       optional = 0;
198     break;
199   case 'n':
200     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
201     snprintf (dest, destlen, tmp, NONULL (query->name));
202     break;
203   case 't':
204     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
205     snprintf (dest, destlen, tmp, entry->tagged ? '*' : ' ');
206     break;
207   default:
208     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
209     snprintf (dest, destlen, tmp, op);
210     break;
211   }
212
213   if (optional)
214     mutt_FormatString (dest, destlen, col, ifstring, query_format_str, data, 0);
215   else if (flags & M_FORMAT_OPTIONAL)
216     mutt_FormatString (dest, destlen, col, elsestring, query_format_str, data, 0);
217
218   return src;
219 }
220
221 static void query_entry (char *s, size_t slen, MUTTMENU *m, int num)
222 {
223   ENTRY *entry = &((ENTRY *) m->data)[num];
224
225   entry->data->num = num;
226   mutt_FormatString (s, slen, 0, NONULL (QueryFormat), query_format_str,
227                      (unsigned long) entry, M_FORMAT_ARROWCURSOR);
228 }
229
230 static int query_tag (MUTTMENU *menu, int n, int m)
231 {
232   ENTRY *cur = &((ENTRY *) menu->data)[n];
233   int ot = cur->tagged;
234   
235   cur->tagged = m >= 0 ? m : !cur->tagged;
236   return cur->tagged - ot;
237 }
238
239 int mutt_query_complete (char *buf, size_t buflen)
240 {
241   QUERY *results = NULL;
242   ADDRESS *tmpa;
243
244   if (!QueryCmd)
245   {
246     mutt_error _("Query command not defined.");
247     return 0;
248   }
249
250   results = run_query (buf, 1);
251   if (results)
252   {
253     /* only one response? */
254     if (results->next == NULL)
255     {
256       tmpa = result_to_addr (results);
257       mutt_addrlist_to_local (tmpa);
258       buf[0] = '\0';
259       rfc822_write_address (buf, buflen, tmpa, 0);
260       rfc822_free_address (&tmpa);
261       mutt_clear_error ();
262       return (0);
263     }
264     /* multiple results, choose from query menu */
265     query_menu (buf, buflen, results, 1);
266   }
267   return (0);
268 }
269
270 void mutt_query_menu (char *buf, size_t buflen)
271 {
272   if (!QueryCmd)
273   {
274     mutt_error _("Query command not defined.");
275     return;
276   }
277
278   if (buf == NULL)
279   {
280     char buffer[STRING] = "";
281
282     query_menu (buffer, sizeof (buffer), NULL, 0);
283   }
284   else
285   {
286     query_menu (buf, buflen, NULL, 1);
287   }
288 }
289
290 static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf)
291 {
292   MUTTMENU *menu;
293   HEADER *msg = NULL;
294   ENTRY *QueryTable = NULL;
295   QUERY *queryp = NULL;
296   int i, done = 0;
297   int op;
298   char helpstr[LONG_STRING];
299   char title[STRING];
300
301   snprintf (title, sizeof (title), _("Query")); /* FIXME */
302
303   menu = mutt_new_menu (MENU_QUERY);
304   menu->make_entry = query_entry;
305   menu->search = query_search;
306   menu->tag = query_tag;
307   menu->title = title;
308   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp);
309
310   if (results == NULL)
311   {
312     /* Prompt for Query */
313     if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0])
314     {
315       results = run_query (buf, 0);
316     }
317   }
318
319   if (results)
320   {
321     snprintf (title, sizeof (title), _("Query '%s'"), buf);
322
323     /* count the number of results */
324     for (queryp = results; queryp; queryp = queryp->next)
325       menu->max++;
326
327     menu->data = QueryTable = (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY));
328
329     for (i = 0, queryp = results; queryp; queryp = queryp->next, i++)
330       QueryTable[i].data = queryp;
331
332     while (!done)
333     {
334       switch ((op = mutt_menuLoop (menu)))
335       {
336         case OP_QUERY_APPEND:
337         case OP_QUERY:
338           if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0])
339           {
340             QUERY *newresults = NULL;
341
342             newresults = run_query (buf, 0);
343
344             menu->redraw = REDRAW_FULL;
345             if (newresults)
346             {
347               snprintf (title, sizeof (title), _("Query '%s'"), buf);
348
349               if (op == OP_QUERY)
350               {
351                 queryp = results;
352                 while (queryp)
353                 {
354                   rfc822_free_address (&queryp->addr);
355                   FREE (&queryp->name);
356                   FREE (&queryp->other);
357                   results = queryp->next;
358                   FREE (&queryp);
359                   queryp = results;
360                 }
361                 results = newresults;
362                 FREE (&QueryTable);
363               }
364               else
365               {
366                 /* append */
367                 for (queryp = results; queryp->next; queryp = queryp->next);
368
369                 queryp->next = newresults;
370               }
371
372
373               menu->current = 0;
374               mutt_menuDestroy (&menu);
375               menu = mutt_new_menu (MENU_QUERY);
376               menu->make_entry = query_entry;
377               menu->search = query_search;
378               menu->tag = query_tag;
379               menu->title = title;
380               menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp);
381
382               /* count the number of results */
383               for (queryp = results; queryp; queryp = queryp->next)
384                 menu->max++;
385
386               if (op == OP_QUERY)
387               {
388                 menu->data = QueryTable = 
389                   (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY));
390
391                 for (i = 0, queryp = results; queryp; 
392                      queryp = queryp->next, i++)
393                   QueryTable[i].data = queryp;
394               }
395               else
396               {
397                 int clear = 0;
398
399                 /* append */
400                 safe_realloc (&QueryTable, menu->max * sizeof (ENTRY));
401
402                 menu->data = QueryTable;
403
404                 for (i = 0, queryp = results; queryp; 
405                      queryp = queryp->next, i++)
406                 {
407                   /* once we hit new entries, clear/init the tag */
408                   if (queryp == newresults)
409                     clear = 1;
410
411                   QueryTable[i].data = queryp;
412                   if (clear)
413                     QueryTable[i].tagged = 0;
414                 }
415               }
416             }
417           }
418           break;
419
420         case OP_CREATE_ALIAS:
421           if (menu->tagprefix)
422           {
423             ADDRESS *naddr = NULL;
424
425             for (i = 0; i < menu->max; i++)
426               if (QueryTable[i].tagged)
427               {
428                 ADDRESS *a = result_to_addr(QueryTable[i].data);
429                 rfc822_append (&naddr, a, 0);
430                 rfc822_free_address (&a);
431               }
432
433             mutt_create_alias (NULL, naddr);
434           }
435           else
436           {
437             ADDRESS *a = result_to_addr(QueryTable[menu->current].data);
438             mutt_create_alias (NULL, a);
439             rfc822_free_address (&a);
440           }
441           break;
442
443         case OP_GENERIC_SELECT_ENTRY:
444           if (retbuf)
445           {
446             done = 2;
447             break;
448           }
449           /* fall through to OP_MAIL */
450
451         case OP_MAIL:
452           msg = mutt_new_header ();
453           msg->env = mutt_new_envelope ();
454           if (!menu->tagprefix)
455           {
456             msg->env->to = result_to_addr(QueryTable[menu->current].data);
457           }
458           else
459           {
460             for (i = 0; i < menu->max; i++)
461               if (QueryTable[i].tagged)
462               {
463                 ADDRESS *a = result_to_addr(QueryTable[i].data);
464                 rfc822_append (&msg->env->to, a, 0);
465                 rfc822_free_address (&a);
466               }
467           }
468           ci_send_message (0, msg, NULL, Context, NULL);
469           menu->redraw = REDRAW_FULL;
470           break;
471
472         case OP_EXIT:
473           done = 1;
474           break;
475       }
476     }
477
478     /* if we need to return the selected entries */
479     if (retbuf && (done == 2))
480     {
481       int tagged = 0;
482       size_t curpos = 0;
483
484       memset (buf, 0, buflen); 
485
486       /* check for tagged entries */
487       for (i = 0; i < menu->max; i++)
488       {
489         if (QueryTable[i].tagged)
490         {
491           if (curpos == 0)
492           {
493             ADDRESS *tmpa = result_to_addr (QueryTable[i].data);
494             mutt_addrlist_to_local (tmpa);
495             tagged = 1;
496             rfc822_write_address (buf, buflen, tmpa, 0);
497             curpos = mutt_strlen (buf);
498             rfc822_free_address (&tmpa);
499           }
500           else if (curpos + 2 < buflen)
501           {
502             ADDRESS *tmpa = result_to_addr (QueryTable[i].data);
503             mutt_addrlist_to_local (tmpa);
504             strcat (buf, ", "); /* __STRCAT_CHECKED__ */
505             rfc822_write_address ((char *) buf + curpos + 1, buflen - curpos - 1,
506                                   tmpa, 0);
507             curpos = mutt_strlen (buf);
508             rfc822_free_address (&tmpa);
509           }
510         }
511       }
512       /* then enter current message */
513       if (!tagged)
514       {
515         ADDRESS *tmpa = result_to_addr (QueryTable[menu->current].data);
516         mutt_addrlist_to_local (tmpa);
517         rfc822_write_address (buf, buflen, tmpa, 0);
518         rfc822_free_address (&tmpa);
519       }
520       
521     }
522
523     queryp = results;
524     while (queryp)
525     {
526       rfc822_free_address (&queryp->addr);
527       FREE (&queryp->name);
528       FREE (&queryp->other);
529       results = queryp->next;
530       FREE (&queryp);
531       queryp = results;
532     }
533     FREE (&QueryTable);
534     
535     /* tell whoever called me to redraw the screen when I return */
536     set_option (OPTNEEDREDRAW);
537   }
538
539   mutt_menuDestroy (&menu);
540 }