Commit b848706b b848706b89934be5a4419ab2ec382157d2f30aba by Sergey Poznyakoff

imap client: initial implementation of fetch.

* include/mailutils/imap.h (mu_imap_fetch): New proto.
(MU_IMAP_CB_FETCH): New callback code.
(MU_IMAP_FETCH_BODY,MU_IMAP_FETCH_BODYSTRUCTURE)
(MU_IMAP_FETCH_ENVELOPE,MU_IMAP_FETCH_FLAGS)
(MU_IMAP_FETCH_INTERNALDATE,MU_IMAP_FETCH_RFC822_SIZE)
(MU_IMAP_FETCH_UID): New constants.
(mu_imap_fetch_body,mu_imap_fetch_bodystructure)
(mu_imap_fetch_envelope,mu_imap_fetch_flags)
(mu_imap_fetch_internaldate,mu_imap_fetch_rfc822_size)
(mu_imap_fetch_uid,mu_imap_fetch_response): New structures.
* include/mailutils/sys/imap.h (MU_IMAP_SET_XSCRIPT_MASK)
(MU_IMAP_CLR_XSCRIPT_MASK): New macros.
(mu_imap_client_state)<MU_IMAP_FETCH_RX>: New state.
(_mu_imap_list_nth_element_is_string): New proto.
* libproto/imap/fetch.c: New file.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Add fetch.c
* libproto/imap/carrier.c (mu_imap_set_carrier): Update call to
mu_imapio_create.
* libproto/imap/resplist.c (_mu_imap_list_nth_element_is_string): New
function.
* libproto/imap/resproc.c (_process_unsolicited_response): Handle FETCH
response.
* libproto/imap/trace.c (mu_imap_trace_mask): Call mu_imapio_trace_payload
if needed.
* mu/imap.c (imap_bad_callback): New function.
(com_disconnect): Register imap_bad_callback.
(com_fetch): New function.
(imap_comtab): Add the "fetch" command.
* mu/mu.h (mutool_command) <flags>: New member.
* mu/pop.c (pop_comtab): Update initializers.
* mu/shell.c (default_comtab): Update initializers.
(execute_line): Rewrite using incremental wordsplit.  This allows for
optionally coalescing arguments with indices >= argmax into one argument.
This is useful for such commands as "prompt" and "fetch".
1 parent 29021e95
...@@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension) ...@@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension)
560 560
561 mu_message_get_header (message, &header); 561 mu_message_get_header (message, &header);
562 562
563
564 /* The subtype. */ 563 /* The subtype. */
565 if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0) 564 if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
566 { 565 {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
21 #include <mailutils/iterator.h> 21 #include <mailutils/iterator.h>
22 #include <mailutils/debug.h> 22 #include <mailutils/debug.h>
23 #include <mailutils/stream.h> 23 #include <mailutils/stream.h>
24 #include <mailutils/util.h>
24 #include <mailutils/kwd.h> 25 #include <mailutils/kwd.h>
25 26
26 #ifdef __cplusplus 27 #ifdef __cplusplus
...@@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc); ...@@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc);
58 59
59 int mu_imap_noop (mu_imap_t imap); 60 int mu_imap_noop (mu_imap_t imap);
60 61
62 int mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items);
63
61 int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier); 64 int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier);
62 int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier); 65 int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier);
63 66
...@@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[]; ...@@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[];
104 107
105 /* The following five callbacks correspond to members of struct 108 /* The following five callbacks correspond to members of struct
106 mu_imap_stat and take a pointer to struct mu_imap_stat as their 109 mu_imap_stat and take a pointer to struct mu_imap_stat as their
107 only argument. */ 110 PDAT argument. SDAT is always 0. */
108 #define MU_IMAP_CB_PERMANENT_FLAGS 0 111 #define MU_IMAP_CB_PERMANENT_FLAGS 0
109 #define MU_IMAP_CB_MESSAGE_COUNT 1 112 #define MU_IMAP_CB_MESSAGE_COUNT 1
110 #define MU_IMAP_CB_RECENT_COUNT 2 113 #define MU_IMAP_CB_RECENT_COUNT 2
...@@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[]; ...@@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[];
113 #define MU_IMAP_CB_UIDVALIDITY 5 116 #define MU_IMAP_CB_UIDVALIDITY 5
114 117
115 /* The following callbacks correspond to server responses and take two 118 /* The following callbacks correspond to server responses and take two
116 argument: a response code (see MU_IMAP_RESPONSE, below), and 119 arguments: a response code (see MU_IMAP_RESPONSE, below) in SDAT, and
117 human-readable text string as returned by the server. The latter can 120 human-readable text string as returned by the server in PDAT. The
118 be NULL. */ 121 latter can be NULL. */
119 #define MU_IMAP_CB_OK 6 122 #define MU_IMAP_CB_OK 6
120 #define MU_IMAP_CB_NO 7 123 #define MU_IMAP_CB_NO 7
121 #define MU_IMAP_CB_BAD 8 124 #define MU_IMAP_CB_BAD 8
122 #define MU_IMAP_CB_BYE 9 125 #define MU_IMAP_CB_BYE 9
123 #define MU_IMAP_CB_PREAUTH 10 126 #define MU_IMAP_CB_PREAUTH 10
124 #define _MU_IMAP_CB_MAX 11 127
128 /* FETCH callback. Arguments: SDAT - message sequence number, PDAT - a
129 list (mu_list_t) of union mu_imap_fetch_response (see below). */
130 #define MU_IMAP_CB_FETCH 11
131
132 #define _MU_IMAP_CB_MAX 12
125 133
126 typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat); 134 typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat);
127 135
...@@ -145,6 +153,91 @@ void mu_imap_register_callback_function (mu_imap_t imap, int code, ...@@ -145,6 +153,91 @@ void mu_imap_register_callback_function (mu_imap_t imap, int code,
145 153
146 extern struct mu_kwd mu_imap_response_codes[]; 154 extern struct mu_kwd mu_imap_response_codes[];
147 155
156 /* FETCH Response Codes */
157
158 /* BODY[<section>]<<origin octet>> */
159 #define MU_IMAP_FETCH_BODY 0
160 /* BODY & BODYSTRUCTURE */
161 #define MU_IMAP_FETCH_BODYSTRUCTURE 1
162 /* ENVELOPE */
163 #define MU_IMAP_FETCH_ENVELOPE 2
164 /* FLAGS */
165 #define MU_IMAP_FETCH_FLAGS 3
166 /* INTERNALDATE */
167 #define MU_IMAP_FETCH_INTERNALDATE 4
168 /* RFC822.SIZE */
169 #define MU_IMAP_FETCH_RFC822_SIZE 5
170 /* UID */
171 #define MU_IMAP_FETCH_UID 6
172
173 struct mu_imap_fetch_body
174 {
175 int type;
176 size_t *partv;
177 size_t partc;
178 char *key;
179 char *text;
180 };
181
182 struct mu_imap_fetch_bodystructure
183 {
184 int type;
185 //FIXME?
186 };
187
188 struct mu_imap_fetch_envelope
189 {
190 int type;
191 struct tm date;
192 struct mu_timezone tz;
193 char *subject;
194 mu_address_t from;
195 mu_address_t sender;
196 mu_address_t reply_to;
197 mu_address_t to;
198 mu_address_t cc;
199 mu_address_t bcc;
200 char *in_reply_to;
201 char *message_id;
202 };
203
204 struct mu_imap_fetch_flags
205 {
206 int type;
207 int flags;
208 };
209
210 struct mu_imap_fetch_internaldate
211 {
212 int type;
213 struct tm tm;
214 struct mu_timezone tz;
215 };
216
217 struct mu_imap_fetch_rfc822_size
218 {
219 int type;
220 size_t size;
221 };
222
223 struct mu_imap_fetch_uid
224 {
225 int type;
226 size_t uid;
227 };
228
229 union mu_imap_fetch_response
230 {
231 int type;
232 struct mu_imap_fetch_body body;
233 struct mu_imap_fetch_bodystructure bodystructure;
234 struct mu_imap_fetch_envelope envelope;
235 struct mu_imap_fetch_flags flags;
236 struct mu_imap_fetch_internaldate internaldate;
237 struct mu_imap_fetch_rfc822_size rfc822_size;
238 struct mu_imap_fetch_uid uid;
239 };
240
148 #ifdef __cplusplus 241 #ifdef __cplusplus
149 } 242 }
150 #endif 243 #endif
......
...@@ -32,6 +32,16 @@ extern "C" { ...@@ -32,6 +32,16 @@ extern "C" {
32 #define MU_IMAP_RESP 0x01 32 #define MU_IMAP_RESP 0x01
33 #define MU_IMAP_TRACE 0x02 33 #define MU_IMAP_TRACE 0x02
34 #define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1)) 34 #define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1))
35 #define MU_IMAP_SET_XSCRIPT_MASK(imap,n) \
36 do \
37 (imap)->flags |= MU_IMAP_XSCRIPT_MASK (n); \
38 while (0)
39 #define MU_IMAP_CLR_XSCRIPT_MASK(imap,n) \
40 do \
41 (imap)->flags &= ~MU_IMAP_XSCRIPT_MASK (n); \
42 while (0)
43 #define MU_IMAP_IS_XSCRIPT_MASK(imap,n) \
44 ((imap)->flags & MU_IMAP_XSCRIPT_MASK (n))
35 45
36 enum mu_imap_client_state 46 enum mu_imap_client_state
37 { 47 {
...@@ -47,6 +57,7 @@ enum mu_imap_client_state ...@@ -47,6 +57,7 @@ enum mu_imap_client_state
47 MU_IMAP_SELECT_RX, 57 MU_IMAP_SELECT_RX,
48 MU_IMAP_STATUS_RX, 58 MU_IMAP_STATUS_RX,
49 MU_IMAP_NOOP_RX, 59 MU_IMAP_NOOP_RX,
60 MU_IMAP_FETCH_RX,
50 MU_IMAP_CLOSING 61 MU_IMAP_CLOSING
51 }; 62 };
52 63
...@@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun, ...@@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun,
174 185
175 int _mu_imap_list_element_is_string (struct imap_list_element *elt, 186 int _mu_imap_list_element_is_string (struct imap_list_element *elt,
176 const char *str); 187 const char *str);
188 int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
189 const char *str);
190
177 int _mu_imap_collect_flags (struct imap_list_element *arg, int *res); 191 int _mu_imap_collect_flags (struct imap_list_element *arg, int *res);
178 192
179 struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx); 193 struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx);
180 194
195 int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list);
181 196
182 # ifdef __cplusplus 197 # ifdef __cplusplus
183 } 198 }
......
...@@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ ...@@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@
28 # url.c 28 # url.c
29 libmu_imap_la_SOURCES = \ 29 libmu_imap_la_SOURCES = \
30 fake-folder.c\ 30 fake-folder.c\
31 fetch.c\
31 callback.c\ 32 callback.c\
32 capability.c\ 33 capability.c\
33 capatst.c\ 34 capatst.c\
......
...@@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier) ...@@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier)
37 if (imap == NULL) 37 if (imap == NULL)
38 return EINVAL; 38 return EINVAL;
39 39
40 rc = mu_imapio_create (&io, carrier); 40 rc = mu_imapio_create (&io, carrier, MU_IMAPIO_CLIENT);
41 if (rc) 41 if (rc)
42 return rc; 42 return rc;
43 if (imap->io) 43 if (imap->io)
......
...@@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx) ...@@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx)
270 return arg; 270 return arg;
271 } 271 }
272 272
273 int
274 _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
275 const char *str)
276 {
277 struct imap_list_element *elt = _mu_imap_list_at (list, n);
278 return elt && elt->type == imap_eltype_string &&
279 strcmp (elt->v.string, str) == 0;
280 }
281
273 282
......
...@@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp) ...@@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp)
304 return 0; 304 return 0;
305 } 305 }
306 } 306 }
307 else if (count == 3 &&
308 _mu_imap_list_nth_element_is_string (resp, 1, "FETCH"))
309 {
310 size_t msgno;
311
312 arg = _mu_imap_list_at (resp, 0);
313 if (arg && arg->type == imap_eltype_string)
314 {
315 char *p;
316 msgno = strtoul (arg->v.string, &p, 10);
317 if (*p)
318 return 1;
319
320 arg = _mu_imap_list_at (resp, 2);
321 if (arg->type == imap_eltype_list)
322 {
323 mu_list_t list;
324
325 if (_mu_imap_parse_fetch_response (arg->v.list, &list) == 0)
326 {
327 mu_imap_callback (imap, MU_IMAP_CB_FETCH, msgno, list);
328 mu_list_destroy (&list);
329 }
330 return 0;
331 }
332 }
333 }
334
307 return 1; 335 return 1;
308 } 336 }
309 337
...@@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, mu_list_t list, ...@@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, mu_list_t list,
317 if (fun) 345 if (fun)
318 fun (imap, list, data); 346 fun (imap, list, data);
319 else 347 else
320 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, 348 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9,
321 ("ignoring unexpected response")); 349 ("ignoring unrecognized response"));
322 } 350 }
323 return 0; 351 return 0;
324 } 352 }
......
...@@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev) ...@@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev)
89 switch (op) 89 switch (op)
90 { 90 {
91 case MU_IMAP_TRACE_SET: 91 case MU_IMAP_TRACE_SET:
92 imap->flags |= MU_IMAP_XSCRIPT_MASK (lev); 92 MU_IMAP_SET_XSCRIPT_MASK (imap, lev);
93 if (lev & MU_XSCRIPT_PAYLOAD)
94 mu_imapio_trace_payload (imap->io, 1);
93 break; 95 break;
94 96
95 case MU_IMAP_TRACE_CLR: 97 case MU_IMAP_TRACE_CLR:
96 imap->flags &= ~MU_IMAP_XSCRIPT_MASK (lev); 98 MU_IMAP_CLR_XSCRIPT_MASK (imap, lev);
99 if (lev & MU_XSCRIPT_PAYLOAD)
100 mu_imapio_trace_payload (imap->io, 0);
97 break; 101 break;
98 102
99 case MU_IMAP_TRACE_QRY: 103 case MU_IMAP_TRACE_QRY:
100 if (imap->flags & MU_IMAP_XSCRIPT_MASK (lev)) 104 if (MU_IMAP_IS_XSCRIPT_MASK (imap, lev))
101 break; 105 break;
102 return MU_ERR_NOENT; 106 return MU_ERR_NOENT;
103 107
104 default: 108 default:
105 return EINVAL; 109 return EINVAL;
106 } 110 }
111
107 return 0; 112 return 0;
108 } 113 }
109 114
......
...@@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void *pdat) ...@@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void *pdat)
182 mu_diag_output (MU_DIAG_INFO, _("server is closing connection")); 182 mu_diag_output (MU_DIAG_INFO, _("server is closing connection"));
183 } 183 }
184 184
185 static void
186 imap_bad_callback (void *data, int code, size_t sdat, void *pdat)
187 {
188 const char *text = pdat;
189 mu_diag_output (MU_DIAG_CRIT, "SERVER ALERT: %s", text);
190 }
191
185 192
186 static int 193 static int
187 com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) 194 com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
...@@ -282,6 +289,9 @@ com_connect (int argc, char **argv) ...@@ -282,6 +289,9 @@ com_connect (int argc, char **argv)
282 mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE, 289 mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE,
283 imap_bye_callback, 290 imap_bye_callback,
284 NULL); 291 NULL);
292 mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD,
293 imap_bad_callback,
294 NULL);
285 295
286 296
287 status = mu_imap_connect (imap); 297 status = mu_imap_connect (imap);
...@@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) ...@@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
587 return 0; 597 return 0;
588 } 598 }
589 599
600 static int
601 com_fetch (int argc, char **argv)
602 {
603 int status = mu_imap_fetch (imap, argv[1], argv[2]);
604 if (status)
605 report_failure ("fetch", status);
606 return 0;
607 }
608
590 struct mutool_command imap_comtab[] = { 609 struct mutool_command imap_comtab[] = {
591 { "capability", 1, -1, com_capability, 610 { "capability", 1, -1, 0,
611 com_capability,
592 /* TRANSLATORS: -reread is a keyword; do not translate. */ 612 /* TRANSLATORS: -reread is a keyword; do not translate. */
593 N_("[-reread] [NAME...]"), 613 N_("[-reread] [NAME...]"),
594 N_("list server capabilities") }, 614 N_("list server capabilities") },
595 { "verbose", 1, 4, com_verbose, 615 { "verbose", 1, 4, 0,
616 com_verbose,
596 "[on|off|mask|unmask] [secure [payload]]", 617 "[on|off|mask|unmask] [secure [payload]]",
597 N_("control the protocol tracing") }, 618 N_("control the protocol tracing") },
598 { "connect", 1, 4, com_connect, 619 { "connect", 1, 4, 0,
620 com_connect,
599 /* TRANSLATORS: --tls is a keyword. */ 621 /* TRANSLATORS: --tls is a keyword. */
600 N_("[-tls] HOSTNAME [PORT]"), 622 N_("[-tls] HOSTNAME [PORT]"),
601 N_("open connection") }, 623 N_("open connection") },
602 { "disconnect", 1, 1, 624 { "disconnect", 1, 1, 0,
603 com_disconnect, 625 com_disconnect,
604 NULL, 626 NULL,
605 N_("close connection") }, 627 N_("close connection") },
606 { "login", 2, 3, com_login, 628 { "login", 2, 3, 0,
629 com_login,
607 N_("USER [PASS]"), 630 N_("USER [PASS]"),
608 N_("login to the server") }, 631 N_("login to the server") },
609 { "logout", 1, 1, com_logout, 632 { "logout", 1, 1, 0,
633 com_logout,
610 NULL, 634 NULL,
611 N_("quit imap session") }, 635 N_("quit imap session") },
612 { "id", 1, -1, com_id, 636 { "id", 1, -1, 0,
637 com_id,
613 N_("[-test KW] [ARG [ARG...]]"), 638 N_("[-test KW] [ARG [ARG...]]"),
614 N_("send ID command") }, 639 N_("send ID command") },
615 { "noop", 1, 1, com_noop, 640 { "noop", 1, 1, 0,
641 com_noop,
616 NULL, 642 NULL,
617 N_("no operation (keepalive)") }, 643 N_("no operation (keepalive)") },
618 { "select", 1, 2, com_select, 644 { "select", 1, 2, 0,
645 com_select,
619 N_("[MBOX]"), 646 N_("[MBOX]"),
620 N_("select a mailbox") }, 647 N_("select a mailbox") },
621 { "examine", 1, 2, com_examine, 648 { "examine", 1, 2, 0,
649 com_examine,
622 N_("[MBOX]"), 650 N_("[MBOX]"),
623 N_("examine a mailbox") }, 651 N_("examine a mailbox") },
624 { "status", 3, -1, com_status, 652 { "status", 3, -1, 0,
653 com_status,
625 N_("MBOX KW [KW...]"), 654 N_("MBOX KW [KW...]"),
626 N_("get mailbox status") }, 655 N_("get mailbox status") },
627 { "quit", 1, 1, com_logout, 656 { "fetch", 3, 3, CMD_COALESCE_EXTRA_ARGS,
657 com_fetch,
658 N_("MSGSET ITEMS"),
659 N_("fetch message data") },
660 { "quit", 1, 1, 0,
661 com_logout,
628 NULL, 662 NULL,
629 N_("same as `logout'") }, 663 N_("same as `logout'") },
630 { NULL } 664 { NULL }
......
...@@ -18,13 +18,15 @@ ...@@ -18,13 +18,15 @@
18 18
19 typedef int (*mutool_action_t) (int argc, char **argv); 19 typedef int (*mutool_action_t) (int argc, char **argv);
20 20
21 #define CMD_COALESCE_EXTRA_ARGS 0x01
22
21 struct mutool_command 23 struct mutool_command
22 { 24 {
23 const char *name; /* User printable name of the function. */ 25 const char *name; /* User printable name of the function. */
24 int argmin; /* Min. acceptable number of arguments (-1 means 26 int argmin; /* Min. acceptable number of arguments (> 1) */
25 pass all arguments as a single string */
26 int argmax; /* Max. allowed number of arguments (-1 means not 27 int argmax; /* Max. allowed number of arguments (-1 means not
27 limited */ 28 limited */
29 int flags;
28 mutool_action_t func; /* Function to call to do the job. */ 30 mutool_action_t func; /* Function to call to do the job. */
29 const char *argdoc; /* Documentation for the arguments */ 31 const char *argdoc; /* Documentation for the arguments */
30 const char *docstring;/* Documentation for this function. */ 32 const char *docstring;/* Documentation for this function. */
......
...@@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) ...@@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
611 } 611 }
612 612
613 struct mutool_command pop_comtab[] = { 613 struct mutool_command pop_comtab[] = {
614 { "apop", 2, 3, com_apop, 614 { "apop", 2, 3, 0, com_apop,
615 N_("USER [PASS]"), 615 N_("USER [PASS]"),
616 N_("authenticate with APOP") }, 616 N_("authenticate with APOP") },
617 { "capa", 1, -1, com_capa, 617 { "capa", 1, -1, 0, com_capa,
618 /* TRANSLATORS: -reread is a keyword; do not translate. */ 618 /* TRANSLATORS: -reread is a keyword; do not translate. */
619 N_("[-reread] [NAME...]"), 619 N_("[-reread] [NAME...]"),
620 N_("list server capabilities") }, 620 N_("list server capabilities") },
621 { "disconnect", 1, 1, 621 { "disconnect", 1, 1, 0,
622 com_disconnect, 622 com_disconnect,
623 NULL, 623 NULL,
624 N_("close connection") }, 624 N_("close connection") },
625 { "dele", 2, 2, com_dele, 625 { "dele", 2, 2, 0, com_dele,
626 N_("NUMBER"), 626 N_("NUMBER"),
627 N_("mark message for deletion") }, 627 N_("mark message for deletion") },
628 { "list", 1, 2, com_list, 628 { "list", 1, 2, 0, com_list,
629 N_("[NUMBER]"), 629 N_("[NUMBER]"),
630 N_("list messages") }, 630 N_("list messages") },
631 { "noop", 1, 1, com_noop, 631 { "noop", 1, 1, 0, com_noop,
632 NULL, 632 NULL,
633 N_("send a \"no operation\"") }, 633 N_("send a \"no operation\"") },
634 { "pass", 1, 2, com_pass, 634 { "pass", 1, 2, 0, com_pass,
635 N_("[PASSWORD]"), 635 N_("[PASSWORD]"),
636 N_("send password") }, 636 N_("send password") },
637 { "connect", 1, 4, com_connect, 637 { "connect", 1, 4, 0, com_connect,
638 /* TRANSLATORS: --tls is a keyword. */ 638 /* TRANSLATORS: --tls is a keyword. */
639 N_("[-tls] HOSTNAME [PORT]"), 639 N_("[-tls] HOSTNAME [PORT]"),
640 N_("open connection") }, 640 N_("open connection") },
641 { "quit", 1, 1, com_quit, 641 { "quit", 1, 1, 0, com_quit,
642 NULL, 642 NULL,
643 N_("quit pop3 session") }, 643 N_("quit pop3 session") },
644 { "retr", 2, 2, com_retr, 644 { "retr", 2, 2, 0, com_retr,
645 "NUMBER", 645 "NUMBER",
646 N_("retrieve a message") }, 646 N_("retrieve a message") },
647 { "rset", 1, 1, com_rset, 647 { "rset", 1, 1, 0, com_rset,
648 NULL, 648 NULL,
649 N_("remove deletion marks") }, 649 N_("remove deletion marks") },
650 { "stat", 1, 1, com_stat, 650 { "stat", 1, 1, 0, com_stat,
651 NULL, 651 NULL,
652 N_("get the mailbox size and number of messages in it") }, 652 N_("get the mailbox size and number of messages in it") },
653 { "stls", 1, 1, com_stls, 653 { "stls", 1, 1, 0, com_stls,
654 NULL, 654 NULL,
655 N_("start TLS negotiation") }, 655 N_("start TLS negotiation") },
656 { "top", 2, 3, com_top, 656 { "top", 2, 3, 0, com_top,
657 "MSGNO [NUMBER]", 657 "MSGNO [NUMBER]",
658 N_("display message headers and first NUMBER (default 5) lines of" 658 N_("display message headers and first NUMBER (default 5) lines of"
659 " its body") }, 659 " its body") },
660 { "uidl", 1, 2, com_uidl, 660 { "uidl", 1, 2, 0, com_uidl,
661 N_("[NUMBER]"), 661 N_("[NUMBER]"),
662 N_("show unique message identifiers") }, 662 N_("show unique message identifiers") },
663 { "user", 2, 2, com_user, 663 { "user", 2, 2, 0, com_user,
664 N_("NAME"), 664 N_("NAME"),
665 N_("send login") }, 665 N_("send login") },
666 { "verbose", 1, 4, com_verbose, 666 { "verbose", 1, 4, 0, com_verbose,
667 "[on|off|mask|unmask] [secure [payload]]", 667 "[on|off|mask|unmask] [secure [payload]]",
668 N_("control the protocol tracing") }, 668 N_("control the protocol tracing") },
669 { NULL } 669 { NULL }
......
...@@ -71,18 +71,18 @@ static int shell_history (int, char **); ...@@ -71,18 +71,18 @@ static int shell_history (int, char **);
71 #endif 71 #endif
72 72
73 struct mutool_command default_comtab[] = { 73 struct mutool_command default_comtab[] = {
74 { "prompt", -1, -1, shell_prompt, 74 { "prompt", 1, 2, CMD_COALESCE_EXTRA_ARGS, shell_prompt,
75 N_("STRING"), 75 N_("STRING"),
76 N_("set command prompt") }, 76 N_("set command prompt") },
77 { "exit", 1, 1, shell_exit, NULL, N_("exit program") }, 77 { "exit", 1, 1, 0, shell_exit, NULL, N_("exit program") },
78 { "help", 1, 2, shell_help, 78 { "help", 1, 2, 0, shell_help,
79 N_("[COMMAND]"), 79 N_("[COMMAND]"),
80 N_("display this text") }, 80 N_("display this text") },
81 { "?", 1, 1, shell_help, 81 { "?", 1, 1, 0, shell_help,
82 N_("[COMMAND]"), 82 N_("[COMMAND]"),
83 N_("synonym for `help'") }, 83 N_("synonym for `help'") },
84 #ifdef WITH_READLINE 84 #ifdef WITH_READLINE
85 { "history", 1, 1, shell_history, 85 { "history", 1, 1, 0, shell_history,
86 NULL, 86 NULL,
87 N_("show command history") }, 87 N_("show command history") },
88 #endif 88 #endif
...@@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED) ...@@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED)
492 } 492 }
493 #endif 493 #endif
494 494
495 static int
496 next_arg (struct mu_wordsplit *ws)
497 {
498 int rc = mu_wordsplit (NULL, ws, MU_WRDSF_INCREMENTAL);
499 if (rc == MU_WRDSE_NOINPUT)
500 {
501 mu_error ("%s: too few arguments", ws->ws_wordv[0]);
502 mu_wordsplit_free (ws);
503 return -1;
504 }
505 else if (rc)
506 {
507 mu_error ("cannot parse input line: %s",
508 mu_wordsplit_strerror (ws));
509 return 1;
510 }
511 return 0;
512 }
513
495 /* Parse and execute a command line. */ 514 /* Parse and execute a command line. */
496 int 515 int
497 execute_line (char *line) 516 execute_line (char *line)
498 { 517 {
518 int rc;
499 struct mu_wordsplit ws; 519 struct mu_wordsplit ws;
500 int argc; 520 struct mutool_command *cmd;
501 char **argv;
502 int status = 0; 521 int status = 0;
503 522
504 ws.ws_comment = "#"; 523 ws.ws_comment = "#";
505 ws.ws_offs = 1; /* Keep extra slot for expansion in case when argmin == -1 */ 524 rc = mu_wordsplit (line, &ws,
506 if (mu_wordsplit (line, &ws, 525 MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|
507 MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|MU_WRDSF_DOOFFS)) 526 MU_WRDSF_INCREMENTAL|MU_WRDSF_APPEND);
527 if (rc == MU_WRDSE_NOINPUT)
528 {
529 mu_wordsplit_free (&ws);
530 return 0;
531 }
532 else if (rc)
508 { 533 {
509 mu_error("cannot parse input line: %s", mu_wordsplit_strerror (&ws)); 534 mu_error ("cannot parse input line: %s", mu_wordsplit_strerror (&ws));
510 return 0; 535 return 0;
511 } 536 }
512 argc = ws.ws_wordc;
513 argv = ws.ws_wordv + 1;
514 537
515 if (argc >= 0) 538 if (ws.ws_wordc)
516 { 539 {
517 struct mutool_command *cmd = find_command (argv[0]); 540 int argmin;
541 cmd = find_command (ws.ws_wordv[0]);
518 542
519 if (!cmd) 543 if (!cmd)
520 mu_error ("%s: no such command.", argv[0]); 544 {
521 else if (cmd->argmin > 0 && argc < cmd->argmin) 545 mu_error ("%s: no such command.", ws.ws_wordv[0]);
522 mu_error ("%s: too few arguments", argv[0]); 546 mu_wordsplit_free (&ws);
523 else if (cmd->argmax > 0 && argc > cmd->argmax) 547 return 0;
524 mu_error ("%s: too many arguments", argv[0]); 548 }
549
550 argmin = cmd->argmin;
551 if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
552 --argmin;
553 while (ws.ws_wordc < argmin)
554 {
555 if (next_arg (&ws))
556 return 0;
557 }
558
559 if (cmd->flags & CMD_COALESCE_EXTRA_ARGS)
560 {
561 ws.ws_flags |= MU_WRDSF_NOSPLIT;
562 if (next_arg (&ws))
563 return 0;
564 }
525 else 565 else
526 { 566 {
527 if (cmd->argmin <= 0 && argc != 2) 567 for (;;)
568 {
569 if (cmd->argmax > 0 && ws.ws_wordc > cmd->argmax)
528 { 570 {
529 size_t i; 571 mu_error ("%s: too many arguments", ws.ws_wordv[0]);
530 char *word = mu_str_skip_class (line, MU_CTYPE_SPACE); 572 mu_wordsplit_free (&ws);
531 char *arg = mu_str_skip_class_comp (word, MU_CTYPE_SPACE); 573 return 0;
532 if (*arg) 574 }
575 rc = mu_wordsplit (NULL, &ws, MU_WRDSF_INCREMENTAL);
576 if (rc == 0)
577 continue;
578 else if (rc == MU_WRDSE_NOINPUT)
579 break;
580 else
533 { 581 {
534 *arg++ = 0; 582 mu_error ("cannot parse input line: %s",
535 arg = mu_str_skip_class (arg, MU_CTYPE_SPACE); 583 mu_wordsplit_strerror (&ws));
584 return 0;
536 } 585 }
537 for (i = 0; i < ws.ws_wordc; i++)
538 free (ws.ws_wordv[i + 1]);
539 ws.ws_wordv[0] = xstrdup (word);
540 ws.ws_wordv[1] = xstrdup (arg);
541 ws.ws_wordv[2] = NULL;
542 ws.ws_wordc = 2;
543 argc = ws.ws_wordc;
544 argv = ws.ws_wordv;
545 } 586 }
546 status = cmd->func (argc, argv);
547 } 587 }
588
589 status = cmd->func (ws.ws_wordc, ws.ws_wordv);
548 } 590 }
549 mu_wordsplit_free (&ws); 591 mu_wordsplit_free (&ws);
550 return status; 592 return status;
......