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