Commit e34d8c16 e34d8c168f9031dbfc2441006e0eb24182ef4389 by Sergey Poznyakoff

mh: fix msgset parser and some more comp compatibility issues; provide testsuite for comp.

* mh/Makefile.am (bin_PROGRAMS): Add mhseq.
* mh/comp.c (main): Rewrite to fix compatibility issues.
* mh/mh.h (MH_MSGSET_UID): New define.
(mh_msgset_t) <flags,size>: New members.
* mh/mh_init.c (mh_draft_message): Bugfix: convert msgset to UIDs.
* mh/mh_msgset.c: Rewrite from scratch.

* mh/tests/comp.at: New file.
* mh/tests/mhseq.at: New file.
* mh/tests/Makefile.am (TESTSUITE_AT): Add comp.at, mhseq.at.
* mh/tests/testsuite.at: Include comp.at and mhseq.at.

* libmailutils/property/mhprop.c (_mh_prop_read_stream): Minor fix.
Do remove empty lines.
* mh/mh_whatnow.c (_whatnow): Detect EOF.
(call_send): Quit after successful send.
1 parent e6927c46
...@@ -113,7 +113,7 @@ _mh_prop_read_stream (mu_header_t *phdr, mu_stream_t stream) ...@@ -113,7 +113,7 @@ _mh_prop_read_stream (mu_header_t *phdr, mu_stream_t stream)
113 argv[1] = "#"; 113 argv[1] = "#";
114 argv[2] = "-r"; 114 argv[2] = "-r";
115 argv[3] = NULL; 115 argv[3] = NULL;
116 rc = mu_filter_create_args (&flt, stream, argv[0], 2, argv, 116 rc = mu_filter_create_args (&flt, stream, argv[0], 3, argv,
117 MU_FILTER_DECODE, MU_STREAM_READ); 117 MU_FILTER_DECODE, MU_STREAM_READ);
118 if (rc) 118 if (rc)
119 { 119 {
......
...@@ -23,6 +23,7 @@ mh_alias_lex.c ...@@ -23,6 +23,7 @@ mh_alias_lex.c
23 mh_fmtgram.c 23 mh_fmtgram.c
24 mhl 24 mhl
25 mhn 25 mhn
26 mhseq
26 mhparam 27 mhparam
27 mhpath 28 mhpath
28 pick 29 pick
......
...@@ -33,6 +33,7 @@ bin_PROGRAMS = \ ...@@ -33,6 +33,7 @@ bin_PROGRAMS = \
33 mhn\ 33 mhn\
34 mhparam\ 34 mhparam\
35 mhpath\ 35 mhpath\
36 mhseq\
36 pick\ 37 pick\
37 prompter\ 38 prompter\
38 refile\ 39 refile\
......
...@@ -80,6 +80,7 @@ static int build_only = 0; /* --build flag */ ...@@ -80,6 +80,7 @@ static int build_only = 0; /* --build flag */
80 static int use_draft = 0; /* --use flag */ 80 static int use_draft = 0; /* --use flag */
81 static char *draftmessage = "new"; 81 static char *draftmessage = "new";
82 static const char *draftfolder = NULL; 82 static const char *draftfolder = NULL;
83 static int folder_set; /* Folder is set on the command line */
83 84
84 static error_t 85 static error_t
85 opt_handler (int key, char *arg, struct argp_state *state) 86 opt_handler (int key, char *arg, struct argp_state *state)
...@@ -87,8 +88,7 @@ opt_handler (int key, char *arg, struct argp_state *state) ...@@ -87,8 +88,7 @@ opt_handler (int key, char *arg, struct argp_state *state)
87 switch (key) 88 switch (key)
88 { 89 {
89 case ARGP_KEY_INIT: 90 case ARGP_KEY_INIT:
90 draftfolder = mh_global_profile_get ("Draft-Folder", 91 draftfolder = mh_global_profile_get ("Draft-Folder", NULL);
91 mu_folder_directory ());
92 whatnowproc = mh_global_profile_get ("whatnowproc", NULL); 92 whatnowproc = mh_global_profile_get ("whatnowproc", NULL);
93 break; 93 break;
94 94
...@@ -106,6 +106,7 @@ opt_handler (int key, char *arg, struct argp_state *state) ...@@ -106,6 +106,7 @@ opt_handler (int key, char *arg, struct argp_state *state)
106 106
107 case ARG_FOLDER: 107 case ARG_FOLDER:
108 mh_set_current_folder (arg); 108 mh_set_current_folder (arg);
109 folder_set = 1;
109 break; 110 break;
110 111
111 case ARG_FORM: 112 case ARG_FORM:
...@@ -126,7 +127,7 @@ opt_handler (int key, char *arg, struct argp_state *state) ...@@ -126,7 +127,7 @@ opt_handler (int key, char *arg, struct argp_state *state)
126 break; 127 break;
127 128
128 case ARG_FILE: 129 case ARG_FILE:
129 wh_env.draftfile = mh_expand_name (NULL, arg, 0); 130 wh_env.file = mh_expand_name (NULL, arg, 0);
130 break; 131 break;
131 132
132 case ARG_NODRAFTFOLDER: 133 case ARG_NODRAFTFOLDER:
...@@ -197,7 +198,7 @@ main (int argc, char **argv) ...@@ -197,7 +198,7 @@ main (int argc, char **argv)
197 mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc, 198 mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc,
198 opt_handler, NULL, &index); 199 opt_handler, NULL, &index);
199 200
200 if (wh_env.draftfile) 201 if (wh_env.file)
201 { 202 {
202 if (build_only) 203 if (build_only)
203 { 204 {
...@@ -205,51 +206,60 @@ main (int argc, char **argv) ...@@ -205,51 +206,60 @@ main (int argc, char **argv)
205 exit (1); 206 exit (1);
206 } 207 }
207 } 208 }
209 else if (folder_set)
210 {
211 wh_env.file = mh_expand_name (NULL, "draft", 0);
212 }
208 else 213 else
209 { 214 {
210 if (build_only || !draftfolder) 215 if (build_only || !draftfolder)
216 {
217 switch (argc - index)
218 {
219 case 0:
211 wh_env.file = mh_expand_name (NULL, "draft", 0); 220 wh_env.file = mh_expand_name (NULL, "draft", 0);
221 break;
222
223 case 1:
224 wh_env.file = mh_expand_name (NULL, argv[index], 0);
225 break;
226
227 default:
228 mu_error (_("only one message at a time!"));
229 return 1;
230 }
231 }
212 else if (draftfolder) 232 else if (draftfolder)
213 { 233 {
214 /* Comp accepts a `file', and it will, if given 234 /* Comp accepts a `file', and it will, if given
215 `-draftfolder +folder' treat this arguments as `msg'. */ 235 `-draftfolder +folder' treat this arguments as `msg'. */
216 if (index < argc) 236 if (use_draft || index < argc)
217 { 237 {
218 mh_msgset_t msgset; 238 mh_msgset_t msgset;
219 mu_mailbox_t mbox; 239 mu_mailbox_t mbox;
220 240
221 mbox = mh_open_folder (draftfolder, 1); 241 mbox = mh_open_folder (draftfolder, 1);
222 mh_msgset_parse (mbox, &msgset, argc - index, argv + index, 242 mh_msgset_parse (mbox, &msgset,
223 "new"); 243 argc - index, argv + index,
244 use_draft ? "cur" : "new");
224 mu_mailbox_destroy (&mbox); 245 mu_mailbox_destroy (&mbox);
225 if (msgset.count != 1) 246 if (msgset.count != 1)
226 { 247 {
227 mu_error (_("only one message at a time!")); 248 mu_error (_("only one message at a time!"));
228 return 1; 249 return 1;
229 } 250 }
251 mh_msgset_uids (mbox, &msgset);
230 draftmessage = mu_umaxtostr (0, msgset.list[0]); 252 draftmessage = mu_umaxtostr (0, msgset.list[0]);
231 mh_msgset_free (&msgset); 253 mh_msgset_free (&msgset);
232 index = argc;
233 } 254 }
234 if (mh_draft_message (draftfolder, draftmessage, 255 if (mh_draft_message (draftfolder, draftmessage,
235 &wh_env.file)) 256 &wh_env.file))
236 return 1; 257 return 1;
237 } 258 }
238 wh_env.draftfile = wh_env.file;
239 } 259 }
260 wh_env.draftfile = wh_env.file;
240 261
241 switch (check_draft_disposition (&wh_env, use_draft)) 262 if (folder_set && index < argc)
242 {
243 case DISP_QUIT:
244 exit (0);
245
246 case DISP_USE:
247 break;
248
249 case DISP_REPLACE:
250 unlink (wh_env.draftfile);
251
252 if (index < argc)
253 { 263 {
254 mh_msgset_t msgset; 264 mh_msgset_t msgset;
255 mu_mailbox_t mbox; 265 mu_mailbox_t mbox;
...@@ -261,13 +271,26 @@ main (int argc, char **argv) ...@@ -261,13 +271,26 @@ main (int argc, char **argv)
261 mu_error (_("only one message at a time!")); 271 mu_error (_("only one message at a time!"));
262 return 1; 272 return 1;
263 } 273 }
274 unlink (wh_env.file);
264 copy_message (mbox, msgset.list[0], wh_env.file); 275 copy_message (mbox, msgset.list[0], wh_env.file);
265 mu_mailbox_destroy (&mbox); 276 mu_mailbox_destroy (&mbox);
266 mh_msgset_free (&msgset); 277 mh_msgset_free (&msgset);
267 } 278 }
268 else 279 else
280 {
281 switch (check_draft_disposition (&wh_env, use_draft))
282 {
283 case DISP_QUIT:
284 exit (0);
285
286 case DISP_USE:
287 break;
288
289 case DISP_REPLACE:
290 unlink (wh_env.draftfile);
269 mh_comp_draft (formfile, "components", wh_env.file); 291 mh_comp_draft (formfile, "components", wh_env.file);
270 } 292 }
293 }
271 294
272 /* Exit immediately if --build is given */ 295 /* Exit immediately if --build is given */
273 if (build_only) 296 if (build_only)
......
...@@ -195,10 +195,14 @@ typedef struct ...@@ -195,10 +195,14 @@ typedef struct
195 mu_header_t header; 195 mu_header_t header;
196 } mh_context_t; 196 } mh_context_t;
197 197
198 #define MH_MSGSET_UID 0x01
199
198 typedef struct 200 typedef struct
199 { 201 {
200 size_t count; 202 int flags;
201 size_t *list; 203 size_t *list;
204 size_t count;
205 size_t size;
202 } mh_msgset_t; 206 } mh_msgset_t;
203 207
204 typedef void (*mh_iterator_fp) (mu_mailbox_t mbox, mu_message_t msg, 208 typedef void (*mh_iterator_fp) (mu_mailbox_t mbox, mu_message_t msg,
...@@ -300,7 +304,7 @@ int mh_message_number (mu_message_t msg, size_t *pnum); ...@@ -300,7 +304,7 @@ int mh_message_number (mu_message_t msg, size_t *pnum);
300 304
301 mu_mailbox_t mh_open_folder (const char *folder, int create); 305 mu_mailbox_t mh_open_folder (const char *folder, int create);
302 306
303 int mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset, 307 void mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset,
304 int argc, char **argv, char *def); 308 int argc, char **argv, char *def);
305 int mh_msgset_member (mh_msgset_t *msgset, size_t num); 309 int mh_msgset_member (mh_msgset_t *msgset, size_t num);
306 void mh_msgset_reverse (mh_msgset_t *msgset); 310 void mh_msgset_reverse (mh_msgset_t *msgset);
......
...@@ -998,14 +998,14 @@ mh_draft_message (const char *name, const char *msgspec, char **pname) ...@@ -998,14 +998,14 @@ mh_draft_message (const char *name, const char *msgspec, char **pname)
998 998
999 argv[0] = (char*) msgspec; 999 argv[0] = (char*) msgspec;
1000 argv[1] = NULL; 1000 argv[1] = NULL;
1001 rc = mh_msgset_parse (mbox, &msgset, 1, argv, "cur"); 1001 mh_msgset_parse (mbox, &msgset, 1, argv, "cur");
1002 if (rc) 1002 if (msgset.count > 1)
1003 mu_error (_("invalid message number: %s"), msgspec);
1004 else if (msgset.count > 1)
1005 mu_error (_("only one message at a time!")); 1003 mu_error (_("only one message at a time!"));
1006 else 1004 else
1005 {
1006 mh_msgset_uids (mbox, &msgset);
1007 uid = msgset.list[0]; 1007 uid = msgset.list[0];
1008 1008 }
1009 mh_msgset_free (&msgset); 1009 mh_msgset_free (&msgset);
1010 } 1010 }
1011 1011
......
...@@ -19,234 +19,316 @@ ...@@ -19,234 +19,316 @@
19 19
20 #include <mh.h> 20 #include <mh.h>
21 21
22 /* Expand a message set (msgcnt;msglist) to accomodate `inc' more 22 int
23 elements */ 23 mh_uid_to_msgno (mu_mailbox_t mbox, size_t uid, size_t *msgno)
24 static void
25 _expand (size_t *msgcnt, size_t **msglist, size_t inc)
26 { 24 {
27 if (!inc) 25 size_t num = mh_get_message (mbox, uid, NULL);
28 return; 26 if (num == 0)
29 27 return MU_ERR_NOENT;
30 *msgcnt += inc; 28 *msgno = num;
31 *msglist = realloc (*msglist, (*msgcnt)*sizeof(**msglist)); 29 return 0;
32 if (!*msglist)
33 mh_err_memory (1);
34 } 30 }
35 31
36 /* Fatal error handler */ 32 int
37 static void 33 mh_msgno_to_uid (mu_mailbox_t mbox, size_t msgno, size_t *uid)
38 msgset_abort (const char *arg)
39 { 34 {
40 mu_error (_("bad message list `%s'"), arg); 35 mu_message_t msg;
41 exit (1); 36 int rc = mu_mailbox_get_message (mbox, msgno, &msg);
37 if (rc)
38 return rc;
39 return mu_message_get_uid (msg, uid);
42 } 40 }
43 41
44 /* Handlers for expansion of the reserved message names */
45 42
46 static int 43 void
47 msgset_first (mu_mailbox_t mbox, size_t *pnum) 44 mh_msgset_init (mh_msgset_t *msgset)
48 { 45 {
49 *pnum = 1; 46 memset (msgset, 0, sizeof (*msgset));
50 return 0;
51 } 47 }
52 48
53 static int 49 void
54 msgset_last (mu_mailbox_t mbox, size_t *pnum) 50 mh_msgset_expand (mh_msgset_t *msgset, size_t count)
55 { 51 {
56 int rc; 52 size_t rest = msgset->size - msgset->count;
57 size_t count = 0;
58 53
59 rc = mu_mailbox_messages_count (mbox, &count); 54 if (rest < count)
60 if (rc)
61 { 55 {
62 mu_error (_("cannot get last message: %s"), mu_strerror (rc)); 56 msgset->size += count;
63 exit (1); 57 msgset->list = xrealloc (msgset->list,
58 msgset->size * sizeof (msgset->list[0]));
64 } 59 }
65 *pnum = count; 60 }
66 return 0; 61
62 void
63 mh_msgset_add (mh_msgset_t *msgset, size_t n)
64 {
65 mh_msgset_expand (msgset, 1);
66 msgset->list[msgset->count++] = n;
67 } 67 }
68 68
69 static int 69 static int
70 msgset_cur (mu_mailbox_t mbox, size_t *pnum) 70 comp_mesg (const void *a, const void *b)
71 { 71 {
72 size_t i, count = 0; 72 size_t an = *(size_t*)a;
73 static int cached_n = 0; 73 size_t bn = *(size_t*)b;
74 size_t cur; 74 if (an > bn)
75 return 1;
76 else if (an < bn)
77 return -1;
78 return 0;
79 }
80
81 void
82 mh_msgset_optimize (mh_msgset_t *msgset)
83 {
84 size_t i, msgno;
85 size_t msgcnt = msgset->count;
86 size_t *msglist = msgset->list;
75 87
76 mh_mailbox_get_cur (mbox, &cur); 88 /* Sort the resulting message set */
89 qsort (msglist, msgcnt, sizeof (*msgset->list), comp_mesg);
77 90
78 if (cached_n) 91 /* Remove duplicates. */
79 { 92 for (i = 0, msgno = 1; i < msgset->count; i++)
80 *pnum = cached_n; 93 if (msglist[msgno-1] != msglist[i])
94 msglist[msgno++] = msglist[i];
95 msgset->count = msgno;
96 }
97
98 /* Check if message with ordinal number `num' is contained in the
99 message set. */
100 int
101 mh_msgset_member (mh_msgset_t *msgset, size_t num)
102 {
103 size_t i;
104
105 for (i = 0; i < msgset->count; i++)
106 if (msgset->list[i] == num)
107 return i + 1;
81 return 0; 108 return 0;
82 } 109 }
83 110
84 mu_mailbox_messages_count (mbox, &count); 111 /* Reverse the order of messages in the message set */
85 for (i = 1; i <= count; i++) 112 void
86 { 113 mh_msgset_reverse (mh_msgset_t *msgset)
87 mu_message_t msg = NULL; 114 {
88 size_t uid = 0; 115 int head, tail;
89 116
90 mu_mailbox_get_message (mbox, i, &msg); 117 for (head = 0, tail = msgset->count-1; head < tail; head++, tail--)
91 mh_message_number (msg, &uid);
92 if (uid == cur)
93 { 118 {
94 *pnum = cached_n = i; 119 size_t val = msgset->list[head];
95 return 0; 120 msgset->list[head] = msgset->list[tail];
96 } 121 msgset->list[tail] = val;
97 } 122 }
98 mu_error (_("no cur message"));
99 exit (1);
100 } 123 }
101 124
102 static int 125 /* Set the current message to that contained at position `index'
103 msgset_prev (mu_mailbox_t mbox, size_t *pnum) 126 in the given message set */
127 void
128 mh_msgset_current (mu_mailbox_t mbox, mh_msgset_t *msgset, int index)
104 { 129 {
105 size_t cur_n = 0; 130 mu_message_t msg = NULL;
106 msgset_cur (mbox, &cur_n); 131 int rc;
107 if (cur_n < 1) 132 size_t cur;
133
134 rc = mu_mailbox_get_message (mbox, msgset->list[index], &msg);
135 if (rc)
108 { 136 {
109 mu_error (_("no prev message")); 137 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, rc);
110 exit (1); 138 exit (1);
111 } 139 }
112 *pnum = cur_n - 1; 140 mh_message_number (msg, &cur);
113 return 0; 141 mh_mailbox_set_cur (mbox, cur);
114 } 142 }
115 143
116 static int 144 /* Free memory allocated for the message set. Note, that the msgset
117 msgset_next (mu_mailbox_t mbox, size_t *pnum) 145 itself is supposed to reside in the statically allocated memory and
146 therefore is not freed */
147 void
148 mh_msgset_free (mh_msgset_t *msgset)
118 { 149 {
119 size_t cur_n = 0, total = 0; 150 if (msgset->count)
120 msgset_cur (mbox, &cur_n); 151 free (msgset->list);
152 }
153
154 /* Negate the message set: on return `msgset' consists of the messages
155 _not contained_ in the input message set. Any memory associated with
156 the input message set is freed */
157 void
158 mh_msgset_negate (mu_mailbox_t mbox, mh_msgset_t *msgset)
159 {
160 size_t i, total = 0, msgno;
161 size_t *list;
162
121 mu_mailbox_messages_count (mbox, &total); 163 mu_mailbox_messages_count (mbox, &total);
122 if (cur_n + 1 > total) 164 list = calloc (total, sizeof (list[0]));
165 if (!list)
166 mh_err_memory (1);
167 for (i = 1, msgno = 0; i <= total; i++)
123 { 168 {
124 mu_error (_("no next message")); 169 if (!mh_msgset_member (msgset, i))
125 exit (1); 170 list[msgno++] = i;
126 } 171 }
127 *pnum = cur_n + 1;
128 return 0;
129 }
130 172
131 static struct msgset_keyword { 173 list = realloc (list, sizeof (list[0]) * msgno);
132 char *name; 174 if (!list)
133 int (*handler) (mu_mailbox_t mbox, size_t *pnum); 175 {
134 } keywords[] = { 176 mu_error (_("not enough memory"));
135 { "first", msgset_first }, 177 abort ();
136 { "last", msgset_last }, 178 }
137 { "prev", msgset_prev }, 179 mh_msgset_free (msgset);
138 { "next", msgset_next }, 180 msgset->count = msgno;
139 { "cur", msgset_cur }, 181 msgset->list = list;
140 { NULL }, 182 }
141 };
142 183
143 /* Preprocess a part of a complex message designation. Returns 184 void
144 a pointer to the allocated memory containing expanded part of 185 mh_msgset_uids (mu_mailbox_t mbox, mh_msgset_t *msgset)
145 the designation. Pointer to the beginning of the not expanded
146 part (in arg) is placed into *rest */
147 static char *
148 msgset_preproc_part (mu_mailbox_t mbox, char *arg, char **rest)
149 { 186 {
150 struct msgset_keyword *p; 187 size_t i;
151 char *cp;
152 188
153 for (p = keywords; p->name; p++) 189 if (msgset->flags & MH_MSGSET_UID)
154 if (strncmp (arg, p->name, strlen (p->name)) == 0) 190 return;
191 for (i = 0; i < msgset->count; i++)
155 { 192 {
156 int rc;
157 size_t uid, num;
158 mu_message_t msg; 193 mu_message_t msg;
194 mu_mailbox_get_message (mbox, msgset->list[i], &msg);
195 mh_message_number (msg, &msgset->list[i]);
196 }
197 msgset->flags |= MH_MSGSET_UID;
198 }
159 199
160 if (p->handler (mbox, &num)) 200 /* Auxiliary function. Performs binary search for a message with the
161 msgset_abort (arg); 201 given sequence number */
162 rc = mu_mailbox_get_message (mbox, num, &msg); 202 static size_t
163 if (rc) 203 mh_search_message (mu_mailbox_t mbox, size_t start, size_t stop,
204 size_t seqno, mu_message_t *mesg)
205 {
206 mu_message_t mid_msg = NULL;
207 size_t num = 0, middle;
208
209 middle = (start + stop) / 2;
210 if (mu_mailbox_get_message (mbox, middle, &mid_msg)
211 || mh_message_number (mid_msg, &num))
212 return 0;
213
214 if (num == seqno)
164 { 215 {
165 mu_error (_("cannot get message %lu: %s"), 216 if (mesg)
166 (unsigned long) num, mu_strerror (rc)); 217 *mesg = mid_msg;
167 exit (1); 218 return middle;
168 }
169 *rest = arg + strlen (p->name);
170 mu_message_get_uid (msg, &uid);
171 return xstrdup (mu_umaxtostr (0, uid));
172 } 219 }
173 cp = strchr (arg, '-');
174 if (cp)
175 {
176 char *ret;
177 220
178 *rest = cp; 221 if (start >= stop)
179 ret = xmalloc (cp - arg + 1); 222 return 0;
180 memcpy (ret, arg, cp - arg);
181 ret[cp - arg] = 0;
182 return ret;
183 }
184 223
185 *rest = arg + strlen (arg); 224 if (num > seqno)
186 return strdup (arg); 225 return mh_search_message (mbox, start, middle-1, seqno, mesg);
226 else /*if (num < seqno)*/
227 return mh_search_message (mbox, middle+1, stop, seqno, mesg);
187 } 228 }
188 229
189 /* Preprocess (expand) a single message designation */ 230 /* Retrieve the message with the given sequence number.
190 static char * 231 Returns ordinal number of the message in the mailbox if found,
191 msgset_preproc (mu_mailbox_t mbox, char *arg) 232 zero otherwise. The retrieved message is stored in the location
233 pointed to by mesg, unless it is NULL. */
234
235 size_t
236 mh_get_message (mu_mailbox_t mbox, size_t seqno, mu_message_t *mesg)
192 { 237 {
193 char *buf, *tail; 238 size_t num, count;
239 mu_message_t msg;
194 240
195 if (strcmp (arg, "all") == 0 || strcmp (arg, ".") == 0) 241 if (mu_mailbox_get_message (mbox, 1, &msg)
242 || mh_message_number (msg, &num))
243 return 0;
244 if (seqno < num)
245 return 0;
246 else if (seqno == num)
196 { 247 {
197 /* Special case */ 248 if (mesg)
198 arg = "first-last"; 249 *mesg = msg;
250 return 1;
199 } 251 }
200 252
201 buf = msgset_preproc_part (mbox, arg, &tail); 253 if (mu_mailbox_messages_count (mbox, &count)
202 if (tail[0] == '-') 254 || mu_mailbox_get_message (mbox, count, &msg)
255 || mh_message_number (msg, &num))
256 return 0;
257 if (seqno > num)
258 return 0;
259 else if (seqno == num)
203 { 260 {
204 char *rest = msgset_preproc_part (mbox, tail+1, &tail); 261 if (mesg)
205 char *p = NULL; 262 *mesg = msg;
206 mu_asprintf (&p, "%s-%s", buf, rest); 263 return count;
207 free (rest);
208 free (buf);
209 buf = p;
210 } 264 }
211 265
212 if (tail[0]) 266 return mh_search_message (mbox, 1, count, seqno, mesg);
213 { 267 }
214 char *p = NULL; 268
215 mu_asprintf (&p, "%s%s", buf, tail); 269
216 free (buf); 270 struct msgset_parser
217 buf = p; 271 {
218 } 272 mu_mailbox_t mbox;
219 return buf; 273 mh_msgset_t *msgset;
274 char *curp;
275 int argc;
276 char **argv;
277
278 int sign;
279 size_t number;
280 int validuid;
281 };
282
283 static void
284 msgset_parser_init (struct msgset_parser *parser, mu_mailbox_t mbox,
285 mh_msgset_t *msgset, int argc, char **argv)
286 {
287 parser->mbox = mbox;
288 parser->msgset = msgset;
289 parser->argc = argc;
290 parser->argv = argv;
291 parser->curp = "";
292
293 parser->sign = 0;
294 parser->number = 0;
295 }
296
297 static void
298 msgset_abort (const char *arg)
299 {
300 mu_error (_("bad message list `%s'"), arg);
301 exit (1);
302 }
303
304 static void
305 emptyrange_abort (const char *range)
306 {
307 mu_error (_("no messages in range %s"), range);
308 exit (1);
220 } 309 }
221 310
311 /* Advance parser to the next argument */
222 static int 312 static int
223 comp_mesg (const void *a, const void *b) 313 nextarg (struct msgset_parser *parser)
224 { 314 {
225 if (*(size_t*)a > *(size_t*)b) 315 if (parser->argc == 0)
226 return 1;
227 else if (*(size_t*)a < *(size_t*)b)
228 return -1;
229 return 0; 316 return 0;
317 parser->argc--;
318 parser->curp = *parser->argv++;
319 return 1;
230 } 320 }
231 321
232 static int _mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset, 322 static void msgset_parser_run (struct msgset_parser *parser);
233 int argc, char **argv);
234 323
235 /* Treat arg as a name of user-defined sequence and attempt to 324 static int
236 expand it. Return 0 if succeeded, non-zero otherwise. */ 325 _expand_sequence (struct msgset_parser *parser, char *term)
237 int
238 expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg)
239 { 326 {
240 struct mu_wordsplit ws; 327 struct mu_wordsplit ws;
241 char *p;
242 const char *listp; 328 const char *listp;
243 int rc = 1;
244 int negate = 0; 329 int negate = 0;
245 330
246 p = strchr (arg, ':'); 331 listp = mh_global_sequences_get (parser->mbox, term, NULL);
247 if (p)
248 *p++ = 0;
249 listp = mh_global_sequences_get (mbox, arg, NULL);
250 if (!listp) 332 if (!listp)
251 { 333 {
252 int len; 334 int len;
...@@ -254,10 +336,10 @@ expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg) ...@@ -254,10 +336,10 @@ expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg)
254 if (!neg) 336 if (!neg)
255 return 1; 337 return 1;
256 len = strlen (neg); 338 len = strlen (neg);
257 if (strncmp (arg, neg, len)) 339 if (strncmp (term, neg, len))
258 return 1; 340 return 1;
259 negate = 1; 341 negate = 1;
260 listp = mh_global_sequences_get (mbox, arg + len, NULL); 342 listp = mh_global_sequences_get (parser->mbox, term + len, NULL);
261 if (!listp) 343 if (!listp)
262 return 1; 344 return 1;
263 } 345 }
...@@ -266,376 +348,375 @@ expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg) ...@@ -266,376 +348,375 @@ expand_user_seq (mu_mailbox_t mbox, mh_msgset_t *msgset, char *arg)
266 { 348 {
267 mu_error (_("cannot split line `%s': %s"), listp, 349 mu_error (_("cannot split line `%s': %s"), listp,
268 mu_wordsplit_strerror (&ws)); 350 mu_wordsplit_strerror (&ws));
351 exit (1);
269 } 352 }
270 else 353 else
271 { 354 {
272 rc = _mh_msgset_parse (mbox, msgset, ws.ws_wordc, ws.ws_wordv); 355 struct msgset_parser clone;
356
357 msgset_parser_init (&clone, parser->mbox, parser->msgset,
358 ws.ws_wordc, ws.ws_wordv);
359 msgset_parser_run (&clone);
273 mu_wordsplit_free (&ws); 360 mu_wordsplit_free (&ws);
274 } 361 }
275 362
276 if (rc)
277 return rc;
278
279 if (negate) 363 if (negate)
280 mh_msgset_negate (mbox, msgset); 364 mh_msgset_negate (parser->mbox, parser->msgset);
365 return 0;
366 }
281 367
282 if (p) 368 static int
369 parse_count (struct msgset_parser *parser)
370 {
371 char *endp;
372 if (!*parser->curp && nextarg (parser) == 0)
373 return 0;
374 if (*parser->curp == '-')
283 { 375 {
284 int first, num; 376 parser->sign = 1;
285 377 parser->curp++;
286 num = strtoul (p, &p, 0);
287 if (*p)
288 {
289 mh_msgset_free (msgset);
290 return 1;
291 } 378 }
292 if (num < 0) 379 else if (*parser->curp == '+')
293 { 380 {
294 first = num + msgset->count; 381 parser->sign = 0;
295 num = - num; 382 parser->curp++;
296 } 383 }
297 else 384 parser->number = strtoul (parser->curp, &endp, 10);
298 first = 0; 385 if (*endp)
299 if (num > msgset->count) 386 msgset_abort (parser->curp);
300 { 387 parser->curp = endp;
301 mh_msgset_free (msgset);
302 return 1; 388 return 1;
303 } 389 }
304 390
305 if (first > 0)
306 memmove (msgset->list, &msgset->list[first],
307 sizeof (msgset->list[0]) * num);
308 msgset->count = num;
309 }
310 391
311 return rc; 392 static int
393 msgset_first (mu_mailbox_t mbox, size_t *pnum)
394 {
395 *pnum = 1;
396 return 0;
312 } 397 }
313 398
314 /* Parse a message specification from (argc;argv). Returned msgset is 399 static int
315 not sorted nor optimised */ 400 msgset_last (mu_mailbox_t mbox, size_t *pnum)
316 int
317 _mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset, int argc, char **argv)
318 { 401 {
319 size_t msgcnt; 402 int rc;
320 size_t *msglist;
321 size_t i, msgno;
322
323 if (argc == 0)
324 return 1;
325 403
326 msgcnt = argc; 404 rc = mu_mailbox_messages_count (mbox, pnum);
327 msglist = calloc (msgcnt, sizeof(*msglist)); 405 if (rc)
328 for (i = 0, msgno = 0; i < argc; i++)
329 { 406 {
330 char *p = NULL, *q; 407 mu_error (_("cannot get last message: %s"), mu_strerror (rc));
331 size_t start, end; 408 exit (1);
332 size_t msg_first, n; 409 }
333 long num; 410 return 0;
334 char *arg = msgset_preproc (mbox, argv[i]); 411 }
335 412
336 if (!mu_isdigit (arg[0])) 413 static int
337 { 414 msgset_cur (mu_mailbox_t mbox, size_t *pnum)
338 int j; 415 {
339 mh_msgset_t m; 416 size_t num;
417 mh_mailbox_get_cur (mbox, &num);
418 return mh_uid_to_msgno (mbox, num, pnum);
419 }
340 420
341 if (expand_user_seq (mbox, &m, arg)) 421 static int
422 msgset_prev (mu_mailbox_t mbox, size_t *pnum)
423 {
424 size_t cur_n = 0;
425 msgset_cur (mbox, &cur_n);
426 if (cur_n < 1)
342 { 427 {
343 mu_error (_("message set %s does not exist"), arg); 428 mu_error (_("no prev message"));
344 exit (1); 429 exit (1);
345 } 430 }
346 _expand (&msgcnt, &msglist, m.count); 431 *pnum = cur_n - 1;
347 for (j = 0; j < m.count; j++) 432 return 0;
348 msglist[msgno++] = m.list[j]; 433 }
349 mh_msgset_free (&m); 434
350 } 435 static int
351 else 436 msgset_next (mu_mailbox_t mbox, size_t *pnum)
352 { 437 {
353 start = strtoul (arg, &p, 0); 438 size_t cur_n = 0, total = 0;
354 switch (*p) 439 msgset_cur (mbox, &cur_n);
355 { 440 mu_mailbox_messages_count (mbox, &total);
356 case 0: 441 if (cur_n + 1 > total)
357 n = mh_get_message (mbox, start, NULL);
358 if (!n)
359 { 442 {
360 mu_error (_("message %lu does not exist"), 443 mu_error (_("no next message"));
361 (unsigned long) start);
362 exit (1); 444 exit (1);
363 } 445 }
364 msglist[msgno++] = n; 446 *pnum = cur_n + 1;
365 break; 447 return 0;
448 }
366 449
367 case '-': 450 struct msgset_keyword
368 end = strtoul (p+1, &p, 0); 451 {
369 if (*p) 452 char *name;
370 msgset_abort (argv[i]); 453 size_t len;
371 if (end < start) 454 int (*handler) (mu_mailbox_t mbox, size_t *pnum);
372 { 455 int sign;
373 size_t t = start; 456 };
374 start = end; 457
375 end = t; 458 static struct msgset_keyword keywords[] = {
376 } 459 #define S(s) #s, sizeof (#s) - 1
377 _expand (&msgcnt, &msglist, end - start); 460 { S(first), msgset_first, 0 },
378 msg_first = msgno; 461 { S(last), msgset_last, 1 },
379 for (; start <= end; start++) 462 { S(prev), msgset_prev, 1 },
380 { 463 { S(next), msgset_next, 0 },
381 n = mh_get_message (mbox, start, NULL); 464 { S(cur), msgset_cur, 0 },
382 if (n) 465 { NULL }
383 msglist[msgno++] = n; 466 };
384 } 467
385 if (msgno == msg_first) 468 #define PARSE_EOF 0
469 #define PARSE_MORE 1
470 #define PARSE_SUCCESS 2
471
472 /* term : NUMBER
473 | "first"
474 | "last"
475 | "cur"
476 | "prev"
477 | "next"
478 ;
479 */
480 static int
481 parse_term (struct msgset_parser *parser, int seq)
482 {
483 size_t tlen;
484 char *term;
485
486 if (!*parser->curp && nextarg (parser) == 0)
487 return PARSE_EOF;
488
489 term = parser->curp;
490 parser->curp = mu_str_skip_class (term, MU_CTYPE_ALPHA|MU_CTYPE_DIGIT);
491 tlen = parser->curp - term;
492 if (mu_isalpha (*term))
386 { 493 {
387 mu_error (_("no messages in range %s"), argv[i]); 494 struct msgset_keyword *p;
388 exit (1);
389 }
390 break;
391 495
392 case ':': 496 for (p = keywords; p->name; p++)
393 num = strtoul (p+1, &q, 0); 497 if (tlen == p->len && memcmp (p->name, term, tlen) == 0)
394 if (*q)
395 msgset_abort (argv[i]);
396 if (p[1] != '+' && p[1] != '-')
397 { 498 {
398 if (strncmp (argv[i], "last:", 5) == 0 499 if (p->handler (parser->mbox, &parser->number))
399 || strncmp (argv[i], "prev:", 5) == 0) 500 msgset_abort (term);
400 num = -num; 501 parser->sign = p->sign;
502 parser->validuid = 1;
503 return PARSE_MORE;
401 } 504 }
402 end = start + num; 505
403 if (end < start) 506 if (*parser->curp == 0 && seq)
404 { 507 {
405 size_t t = start; 508 /* See if it is a user-defined sequence */
406 start = end + 1; 509 if (_expand_sequence (parser, term) == 0)
407 end = t; 510 return PARSE_SUCCESS;
408 } 511 }
409 else 512 msgset_abort (term);
410 end--;
411 _expand (&msgcnt, &msglist, end - start);
412 msg_first = msgno;
413 for (; start <= end; start++)
414 {
415 n = mh_get_message (mbox, start, NULL);
416 if (n)
417 msglist[msgno++] = n;
418 } 513 }
419 if (msgno == msg_first) 514 else if (mu_isdigit (*term))
420 { 515 {
421 mu_error (_("no messages in range %s"), argv[i]); 516 char *endp;
422 exit (1); 517 size_t num = strtoul (term, &endp, 10);
423 } 518 if (endp != parser->curp)
424 break; 519 msgset_abort (term);
425 520
426 default: 521 if (mh_uid_to_msgno (parser->mbox, num, &parser->number))
427 msgset_abort (argv[i]); 522 {
428 } 523 parser->validuid = 0;
524 parser->number = num;
429 } 525 }
430 free (arg); 526 else
527 parser->validuid = 1;
528 parser->sign = 0;
431 } 529 }
432 530 else
433 msgset->count = msgno; 531 msgset_abort (term);
434 msgset->list = msglist; 532 return PARSE_MORE;
435 return 0;
436 } 533 }
437 534
438 /* Parse a message specification from (argc;argv). Returned msgset is 535 static void
439 sorted and optimised (i.e. it does not contain duplicate message 536 add_messages (struct msgset_parser *parser, size_t start, size_t count,
440 numbers) */ 537 int sign)
441 int
442 mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset,
443 int argc, char **argv, char *def)
444 { 538 {
445 char *xargv[2]; 539 size_t i;
446 int rc;
447 540
448 if (argc == 0) 541 if (start == 0)
542 start = 1;
543 mh_msgset_expand (parser->msgset, count);
544 if (sign)
449 { 545 {
450 argc = 1; 546 if (count > start)
451 argv = xargv; 547 count = start;
452 argv[0] = def ? def : "cur"; 548 for (i = 0; i < count; i++, start--)
453 argv[1] = NULL; 549 parser->msgset->list[parser->msgset->count++] = start;
454 } 550 }
455 551 else
456 rc = _mh_msgset_parse (mbox, msgset, argc, argv);
457
458 if (rc == 0)
459 { 552 {
460 size_t i, msgno; 553 size_t total;
461 size_t msgcnt = msgset->count;
462 size_t *msglist = msgset->list;
463 554
464 /* Sort the resulting message set */ 555 mu_mailbox_messages_count (parser->mbox, &total);
465 qsort (msglist, msgcnt, sizeof (*msgset->list), comp_mesg); 556 if (start + count > total)
466 557 count = total - start + 1;
467 /* Remove duplicates. */ 558 for (i = 0; i < count; i++, start++)
468 for (i = 0, msgno = 1; i < msgset->count; i++) 559 parser->msgset->list[parser->msgset->count++] = start;
469 if (msglist[msgno-1] != msglist[i])
470 msglist[msgno++] = msglist[i];
471 msgset->count = msgno;
472 } 560 }
473 return rc; 561 if (count == 0)
562 emptyrange_abort (parser->argv[-1]);
474 } 563 }
475 564
476 /* Check if message with ordinal number `num' is contained in the 565 static void
477 message set. */ 566 add_message_range (struct msgset_parser *parser, size_t start, size_t end)
478 int
479 mh_msgset_member (mh_msgset_t *msgset, size_t num)
480 { 567 {
481 size_t i; 568 if (end == start)
569 emptyrange_abort (parser->argv[-1]);
482 570
483 for (i = 0; i < msgset->count; i++) 571 if (end < start)
484 if (msgset->list[i] == num) 572 {
485 return i + 1; 573 size_t t = start;
486 return 0; 574 start = end;
575 end = t;
576 }
577 mh_msgset_expand (parser->msgset, end - start + 1);
578
579 for (; start <= end; start++)
580 parser->msgset->list[parser->msgset->count++] = start;
487 } 581 }
488 582
489 /* Auxiliary function. Performs binary search for a message with the 583 /* range: term '-' term
490 given sequence number */ 584 | term ':' count
491 static size_t 585 ;
492 mh_search_message (mu_mailbox_t mbox, size_t start, size_t stop, 586 count: NUMBER
493 size_t seqno, mu_message_t *mesg) 587 | '+' NUMBER
588 | '-' NUMBER
589 ;
590 */
591 static int
592 parse_range (struct msgset_parser *parser)
494 { 593 {
495 mu_message_t mid_msg = NULL; 594 size_t start;
496 size_t num = 0, middle;
497
498 middle = (start + stop) / 2;
499 if (mu_mailbox_get_message (mbox, middle, &mid_msg)
500 || mh_message_number (mid_msg, &num))
501 return 0;
502 595
503 if (num == seqno) 596 switch (parse_term (parser, 1))
504 { 597 {
505 if (mesg) 598 case PARSE_EOF:
506 *mesg = mid_msg;
507 return middle;
508 }
509
510 if (start >= stop)
511 return 0; 599 return 0;
512 600
513 if (num > seqno) 601 case PARSE_SUCCESS:
514 return mh_search_message (mbox, start, middle-1, seqno, mesg); 602 return 1;
515 else /*if (num < seqno)*/
516 return mh_search_message (mbox, middle+1, stop, seqno, mesg);
517 }
518 603
519 /* Retrieve the message with the given sequence number. 604 case PARSE_MORE:
520 Returns ordinal number of the message in the mailbox if found, 605 break;
521 zero otherwise. The retrieved message is stored in the location 606 }
522 pointed to by mesg, unless it is NULL. */
523 607
524 size_t 608 start = parser->number;
525 mh_get_message (mu_mailbox_t mbox, size_t seqno, mu_message_t *mesg)
526 {
527 size_t num, count;
528 mu_message_t msg;
529 609
530 if (mu_mailbox_get_message (mbox, 1, &msg) 610 if (*parser->curp == ':')
531 || mh_message_number (msg, &num)) 611 {
532 return 0; 612 int validuid = parser->validuid;
533 if (seqno < num) 613 parser->curp++;
614 if (parse_count (parser) == 0)
534 return 0; 615 return 0;
535 else if (seqno == num) 616 if (!validuid)
536 { 617 {
537 if (mesg) 618 size_t total, lastuid;
538 *mesg = msg; 619 msgset_last (parser->mbox, &total);
620 mh_msgno_to_uid (parser->mbox, total, &lastuid);
621 if (start > lastuid)
622 {
623 if (!parser->sign)
624 emptyrange_abort (parser->argv[-1]);
625 start = total;
626 }
627 else
628 {
629 if (parser->sign)
630 emptyrange_abort (parser->argv[-1]);
631 start = 1;
632 }
633 }
634 add_messages (parser, start, parser->number, parser->sign);
539 return 1; 635 return 1;
540 } 636 }
637 else if (*parser->curp == '-')
638 {
639 size_t lastuid = 0;
640 int validuid = parser->validuid;
541 641
542 if (mu_mailbox_messages_count (mbox, &count) 642 parser->curp++;
543 || mu_mailbox_get_message (mbox, count, &msg) 643 if (parse_term (parser, 0) == PARSE_EOF)
544 || mh_message_number (msg, &num))
545 return 0;
546 if (seqno > num)
547 return 0; 644 return 0;
548 else if (seqno == num) 645 if (!parser->validuid)
549 { 646 {
550 if (mesg) 647 size_t total;
551 *mesg = msg;
552 return count;
553 }
554
555 return mh_search_message (mbox, 1, count, seqno, mesg);
556 }
557 648
558 /* Reverse the order of messages in the message set */ 649 msgset_last (parser->mbox, &total);
559 void 650 mh_msgno_to_uid (parser->mbox, total, &lastuid);
560 mh_msgset_reverse (mh_msgset_t *msgset) 651 if (parser->number > lastuid)
561 { 652 parser->number = total;
562 int head, tail; 653 else if (!validuid)
563 654 emptyrange_abort (parser->argv[-1]);
564 for (head = 0, tail = msgset->count-1; head < tail; head++, tail--) 655 }
656 if (!validuid)
565 { 657 {
566 size_t val = msgset->list[head]; 658 if (!lastuid)
567 msgset->list[head] = msgset->list[tail]; 659 {
568 msgset->list[tail] = val; 660 size_t total;
661 msgset_last (parser->mbox, &total);
662 mh_msgno_to_uid (parser->mbox, total, &lastuid);
569 } 663 }
570 } 664 if (start > lastuid && !parser->validuid)
571 665 emptyrange_abort (parser->argv[-1]);
572 /* Set the current message to that contained at position `index' 666 start = 1;
573 in the given message set */ 667 }
574 void 668 add_message_range (parser, start, parser->number);
575 mh_msgset_current (mu_mailbox_t mbox, mh_msgset_t *msgset, int index) 669 }
576 { 670 else if (!parser->validuid)
577 mu_message_t msg = NULL;
578 int rc;
579 size_t cur;
580
581 rc = mu_mailbox_get_message (mbox, msgset->list[index], &msg);
582 if (rc)
583 { 671 {
584 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, rc); 672 mu_error (_("message %s does not exist"), parser->argv[-1]);
585 exit (1); 673 exit (1);
586 } 674 }
587 mh_message_number (msg, &cur); 675 else
588 mh_mailbox_set_cur (mbox, cur); 676 mh_msgset_add (parser->msgset, start);
677 return 1;
589 } 678 }
590 679
591 /* Free memory allocated for the message set. Note, that the msgset 680
592 itself is supposed to reside in the statically allocated memory and 681 /* Parse a message specification. The composed msgset is
593 therefore is not freed */ 682 not sorted nor optimised */
594 void 683 static void
595 mh_msgset_free (mh_msgset_t *msgset) 684 msgset_parser_run (struct msgset_parser *parser)
596 { 685 {
597 if (msgset->count) 686 while (parse_range (parser))
598 free (msgset->list); 687 ;
599 } 688 }
600 689
601 /* Negate the message set: on return `msgset' consists of the messages 690 /* Parse a message specification from (argc;argv). Returned msgset is
602 _not contained_ in the input message set. Any memory associated with 691 sorted and optimised (i.e. it does not contain duplicate message
603 the input message set is freed */ 692 numbers) */
604 void 693 void
605 mh_msgset_negate (mu_mailbox_t mbox, mh_msgset_t *msgset) 694 mh_msgset_parse (mu_mailbox_t mbox, mh_msgset_t *msgset,
695 int argc, char **argv, char *def)
606 { 696 {
607 size_t i, total = 0, msgno; 697 struct msgset_parser parser;
608 size_t *list; 698 char *xargv[2];
609 699
610 mu_mailbox_messages_count (mbox, &total); 700 if (argc == 0)
611 list = calloc (total, sizeof (list[0]));
612 if (!list)
613 mh_err_memory (1);
614 for (i = 1, msgno = 0; i <= total; i++)
615 { 701 {
616 if (!mh_msgset_member (msgset, i)) 702 argc = 1;
617 list[msgno++] = i; 703 argv = xargv;
704 argv[0] = def ? def : "cur";
705 argv[1] = NULL;
618 } 706 }
619 707
620 list = realloc (list, sizeof (list[0]) * msgno); 708 if (argc == 1 &&
621 if (!list) 709 (strcmp (argv[0], "all") == 0 || strcmp (argv[0], ".") == 0))
622 { 710 {
623 mu_error (_("not enough memory")); 711 argc = 1;
624 abort (); 712 argv = xargv;
713 argv[0] = "first-last";
714 argv[1] = NULL;
625 } 715 }
626 mh_msgset_free (msgset);
627 msgset->count = msgno;
628 msgset->list = list;
629 }
630 716
631 void 717 mh_msgset_init (msgset);
632 mh_msgset_uids (mu_mailbox_t mbox, mh_msgset_t *msgset) 718 msgset_parser_init (&parser, mbox, msgset, argc, argv);
633 { 719 msgset_parser_run (&parser);
634 size_t i; 720
635 for (i = 0; i < msgset->count; i++) 721 mh_msgset_optimize (msgset);
636 {
637 mu_message_t msg;
638 mu_mailbox_get_message (mbox, msgset->list[i], &msg);
639 mh_message_number (msg, &msgset->list[i]);
640 }
641 } 722 }
......
...@@ -346,17 +346,20 @@ _whatnow (struct mh_whatnow_env *wh, struct action_tab *tab) ...@@ -346,17 +346,20 @@ _whatnow (struct mh_whatnow_env *wh, struct action_tab *tab)
346 346
347 do 347 do
348 { 348 {
349 size_t n;
349 handler_fp fun; 350 handler_fp fun;
350 351
351 printf ("%s ", wh->prompt); 352 printf ("%s ", wh->prompt);
352 fflush (stdout); 353 fflush (stdout);
353 rc = mu_stream_getline (in, &line, &size, NULL); 354 rc = mu_stream_getline (in, &line, &size, &n);
354 if (rc) 355 if (rc)
355 { 356 {
356 mu_error (_("cannot read input stream: %s"), mu_strerror (rc)); 357 mu_error (_("cannot read input stream: %s"), mu_strerror (rc));
357 status = 1; 358 status = 1;
358 break; 359 break;
359 } 360 }
361 if (n == 0)
362 break;
360 363
361 ws.ws_comment = "#"; 364 ws.ws_comment = "#";
362 rc = mu_wordsplit (line, &ws, wsflags); 365 rc = mu_wordsplit (line, &ws, wsflags);
...@@ -462,7 +465,10 @@ static int ...@@ -462,7 +465,10 @@ static int
462 call_send (struct mh_whatnow_env *wh, int argc, char **argv, int *status) 465 call_send (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
463 { 466 {
464 if (invoke ("sendproc", MHBINDIR "/send", argc, argv, wh->file, NULL) == 0) 467 if (invoke ("sendproc", MHBINDIR "/send", argc, argv, wh->file, NULL) == 0)
468 {
465 annotate (wh); 469 annotate (wh);
470 return 1;
471 }
466 return 0; 472 return 0;
467 } 473 }
468 474
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2010 Free Software Foundation, Inc.
3
4 GNU Mailutils 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 3, or (at your option)
7 any later version.
8
9 GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 /* MH mhparam command */
18
19 #include <mh.h>
20
21 static char doc[] = N_("GNU MH mhseq")"\v"
22 N_("Use -help to obtain the list of traditional MH options.");
23 static char args_doc[] = N_("[SEQUENCE]");
24
25 static struct argp_option options[] = {
26 {"folder", ARG_FOLDER, N_("FOLDER"), 0,
27 N_("specify the folder to use")},
28 { "uids", 'u', NULL, 0,
29 N_("show message UIDs (default)")},
30 { "numbers", 'n', NULL, 0,
31 N_("show message numbers") },
32 { NULL }
33 };
34
35 /* Traditional MH options */
36 struct mh_option mh_option[] = {
37 { "uid" },
38 { NULL }
39 };
40
41 static int uid_option = 1;
42
43 static error_t
44 opt_handler (int key, char *arg, struct argp_state *state)
45 {
46 switch (key)
47 {
48 case ARG_FOLDER:
49 mh_set_current_folder (arg);
50 break;
51
52 case 'n':
53 uid_option = 0;
54 break;
55
56 case 'u':
57 uid_option = 1;
58 break;
59
60 default:
61 return ARGP_ERR_UNKNOWN;
62 }
63 return 0;
64 }
65
66 int
67 main (int argc, char **argv)
68 {
69 int index;
70 mu_mailbox_t mbox;
71 mh_msgset_t msgset;
72 size_t i;
73
74 /* Native Language Support */
75 MU_APP_INIT_NLS ();
76
77 mh_argp_init ();
78 mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc,
79 opt_handler, NULL, &index);
80
81 argc -= index;
82 argv += index;
83 mbox = mh_open_folder (mh_current_folder (), 0);
84
85 mh_msgset_parse (mbox, &msgset, argc, argv, "cur");
86 if (uid_option)
87 mh_msgset_uids (mbox, &msgset);
88
89 for (i = 0; i < msgset.count; i++)
90 printf ("%lu\n", (unsigned long) msgset.list[i]);
91 return 0;
92 }
93
94
...@@ -42,6 +42,7 @@ TESTSUITE_AT = \ ...@@ -42,6 +42,7 @@ TESTSUITE_AT = \
42 ali.at\ 42 ali.at\
43 anno.at\ 43 anno.at\
44 burst.at\ 44 burst.at\
45 comp.at\
45 folder.at\ 46 folder.at\
46 inc.at\ 47 inc.at\
47 install-mh.at\ 48 install-mh.at\
...@@ -49,6 +50,7 @@ TESTSUITE_AT = \ ...@@ -49,6 +50,7 @@ TESTSUITE_AT = \
49 mhl.at\ 50 mhl.at\
50 mhparam.at\ 51 mhparam.at\
51 mhpath.at\ 52 mhpath.at\
53 mhseq.at\
52 pick.at\ 54 pick.at\
53 scan.at\ 55 scan.at\
54 refile.at\ 56 refile.at\
......
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2010 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 m4_pushdef([MH_KEYWORDS],[comp])
18 m4_pushdef([compcmd],[comp dnl
19 -form $abs_top_srcdir/mh/components dnl
20 -editor $abs_top_srcdir/mh/tests/mhed])
21
22 MH_CHECK([comp -file],[comp00 comp-file],[
23 dir=`pwd`
24 echo quit | compcmd -file ./infile | sed "s|$dir/*||;s| *$||"
25 cat infile
26 ],
27 [0],
28 [-- Editor invocation: ./infile
29 -- Input file:
30 To:
31 cc:
32 Subject:
33 --------
34 -- Input file end
35 What now? draft left on "./infile".
36 To:
37 cc:
38 Subject:
39 --------
40 Seen by mhed
41 ])
42
43 MH_CHECK([comp -file (del)],[comp01 comp-file_del],[
44 dir=`pwd`
45 echo 'quit -delete' | compcmd -file ./infile | sed "s|$dir/*||;s| *$||"
46 ],
47 [0],
48 [-- Editor invocation: ./infile
49 -- Input file:
50 To:
51 cc:
52 Subject:
53 --------
54 -- Input file end
55 What now?])
56
57 MH_CHECK([comp file],[comp02 comp_file],[
58 echo 'quit' | compcmd file | sed "s|$dir/*||;s| *$||"
59 cat Mail/file
60 ],
61 [0],
62 [-- Editor invocation: Mail/file
63 -- Input file:
64 To:
65 cc:
66 Subject:
67 --------
68 -- Input file end
69 What now? draft left on "Mail/file".
70 To:
71 cc:
72 Subject:
73 --------
74 Seen by mhed
75 ])
76
77 MH_CHECK([comp -use file],[comp03 comp-use_file],[
78 AT_DATA([Mail/file],[From: gray
79 To: root
80 Subject: test input
81
82 message body
83 ])
84
85 echo 'quit' | compcmd -use file | sed "s|$dir/*||;s| *$||"
86 cat Mail/file
87 ],
88 [0],
89 [-- Editor invocation: Mail/file
90 -- Input file:
91 From: gray
92 To: root
93 Subject: test input
94
95 message body
96 -- Input file end
97 What now? draft left on "Mail/file".
98 From: gray
99 To: root
100 Subject: test input
101
102 message body
103 Seen by mhed
104 ])
105
106 MH_CHECK([comp +folder msg],[comp04 comp+folder_msg],[
107 mkdir Mail/inbox
108 AT_DATA([Mail/inbox/1],[From: gray
109 To: root
110 Subject: test input
111
112 message body
113 ])
114
115 echo 'quit' | compcmd +inbox 1 | sed "s|$dir/*||;s| *$||"
116 echo Mail/draft
117 cat Mail/draft
118 echo Message
119 cat Mail/inbox/1
120 ],
121 [0],
122 [-- Editor invocation: Mail/draft
123 -- Input file:
124 From: gray
125 To: root
126 Subject: test input
127
128 message body
129 -- Input file end
130 What now? draft left on "Mail/draft".
131 Mail/draft
132 From: gray
133 To: root
134 Subject: test input
135
136 message body
137 Seen by mhed
138 Message
139 From: gray
140 To: root
141 Subject: test input
142
143 message body
144 ])
145
146 m4_popdef([compcmd])
147 m4_popdef([MH_KEYWORDS])
148
1 #! /bin/sh
2
3 echo "-- Editor invocation:" $*
4 echo "-- Input file: "
5 cat $1
6 echo "-- Input file end"
7 echo "Seen by mhed" >> $1
8 exit 0
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2010 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 m4_pushdef([MH_KEYWORDS],[mhseq mh-sequences])
18
19 dnl ---------------------------------------------------------------
20
21 MH_CHECK([mhseq: existing message number],[mhseq00],[
22 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
23 mhseq 1
24 ],
25 [0],
26 [1
27 ])
28
29 MH_CHECK([mhseq: not existing message number],[mhseq01],[
30 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
31 mhseq 100
32 ],
33 [1],
34 [],
35 dnl FIXME: See FIXME 3 in mhpath.at
36 [mhseq: message 100 does not exist
37 ])
38
39 MH_CHECK([mhseq: contiguous message range],[mhseq02],[
40 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
41 mhseq 2-5
42 ],
43 [0],
44 [2
45 3
46 4
47 5
48 ])
49
50 MH_CHECK([mhseq: reversed contiguous message range],[mhseq03],[
51 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
52 mhseq 5-2
53 ],
54 [0],
55 [2
56 3
57 4
58 5
59 ])
60
61 MH_CHECK([mhseq: reversed non-contiguous message range],[mhseq04],[
62 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
63 rm Mail/inbox/3 Mail/inbox/4
64 mhseq 5-2
65 ],
66 [0],
67 [2
68 5
69 ])
70
71 MH_CHECK([mhseq: message range (left fixup)],[mhseq05],[
72 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
73 rm Mail/inbox/1 Mail/inbox/2
74 mhseq 1-5
75 ],
76 [0],
77 [3
78 4
79 5
80 ])
81
82 MH_CHECK([mhseq: message range (right fixup)],[mhseq06],[
83 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
84 rm Mail/inbox/4 Mail/inbox/5
85 mhseq 1-5
86 ],
87 [0],
88 [1
89 2
90 3
91 ])
92
93 MH_CHECK([mhseq: message range (both fixups)],[mhseq07],[
94 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
95 rm Mail/inbox/1 Mail/inbox/5
96 mhseq 1-5
97 ],
98 [0],
99 [2
100 3
101 4
102 ])
103
104 MH_CHECK([mhseq: non-existent message range (left)],[mhseq08],[
105 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
106 rm Mail/inbox/1 Mail/inbox/2 Mail/inbox/3
107 mhseq 1-2
108 ],
109 [1],
110 [],
111 [mhseq: no messages in range 1-2
112 ])
113
114 MH_CHECK([mhseq: non-existent message range (right)],[mhseq09],[
115 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
116 mhseq 6-10
117 ],
118 [1],
119 [],
120 [mhseq: no messages in range 6-10
121 ])
122
123 MH_CHECK([mhseq: message set addition],[mhseq10],[
124 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
125 mhseq 5 8-10 15-20
126 ],
127 [0],
128 [5
129 8
130 9
131 10
132 15
133 16
134 17
135 18
136 19
137 20
138 ])
139
140 MH_CHECK([mhseq: message set optimization],[mhseq11],[
141 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
142 rm Mail/inbox/17 Mail/inbox/19
143 mhseq 5 1-10 15-20
144 ],
145 [0],
146 [1
147 2
148 3
149 4
150 5
151 6
152 7
153 8
154 9
155 10
156 15
157 16
158 18
159 20
160 ])
161
162 MH_CHECK([mhseq: counted range (positive)],[mhseq12],[
163 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
164 mhseq 10:4
165 ],
166 [0],
167 [10
168 11
169 12
170 13
171 ])
172
173 MH_CHECK([mhseq: non-contiguous counted range (positive)],[mhseq13],[
174 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
175 rm Mail/inbox/12 Mail/inbox/14
176 mhseq 10:4
177 ],
178 [0],
179 [10
180 11
181 13
182 15
183 ])
184
185 MH_CHECK([mhseq: counted range (positive, left fixup)],[mhseq14],[
186 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
187 rm Mail/inbox/1 Mail/inbox/2
188 mhseq 1:3
189 ],
190 [0],
191 [3
192 4
193 5
194 ])
195
196 MH_CHECK([mhseq: counted range (positive, right fixup)],[mhseq15],[
197 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
198 mhseq 2:10
199 ],
200 [0],
201 [2
202 3
203 4
204 5
205 ])
206
207 MH_CHECK([mhseq: invalid counted range (negative)],[mhseq16],[
208 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
209 rm Mail/inbox/1 Mail/inbox/2
210 mhseq 10:2
211 ],
212 [1],
213 [],
214 [mhseq: no messages in range 10:2
215 ])
216
217 MH_CHECK([mhseq: counted range (negative)],[mhseq17],[
218 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
219 mhseq 10:-4
220 ],
221 [0],
222 [7
223 8
224 9
225 10
226 ])
227
228 MH_CHECK([mhseq: non-contiguous counted range (negative)],[mhseq18],[
229 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/teaparty,[Mail/inbox])
230 rm Mail/inbox/8 Mail/inbox/6
231 mhseq 10:-4
232 ],
233 [0],
234 [5
235 7
236 9
237 10
238 ])
239
240 MH_CHECK([mhseq: counted range (negative, left fixup)],[mhseq19],[
241 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
242 rm Mail/inbox/1 Mail/inbox/2
243 mhseq 4:-30
244 ],
245 [0],
246 [3
247 4
248 ])
249
250 MH_CHECK([mhseq: counted range (negative, right fixup)],[mhseq20],[
251 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
252 rm Mail/inbox/1 Mail/inbox/2
253 mhseq 4:30
254 ],
255 [0],
256 [4
257 5
258 ])
259
260 MH_CHECK([mhseq: invalid counted range (negative)],[mhseq21],[
261 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
262 rm Mail/inbox/1 Mail/inbox/2
263 mhseq 1:-30
264 ],
265 [1],
266 [],
267 [mhseq: no messages in range 1:-30
268 ])
269
270 MH_CHECK([mhseq: cur],[mhseq22 cur],[
271 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
272 echo "cur: 3" > Mail/inbox/.mh_sequences
273 mhseq cur
274 ],
275 [0],
276 [3
277 ])
278
279 MH_CHECK([mhseq: cur:n -- default direction],[mhseq23 cur:n],[
280 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
281 echo "cur: 3" > Mail/inbox/.mh_sequences
282 mhseq cur:2
283 ],
284 [0],
285 [3
286 4
287 ])
288
289 MH_CHECK([mhseq: cur:n -- explicit dir (negative)],[mhseq24 cur:n cur:n-],[
290 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
291 echo "cur: 3" > Mail/inbox/.mh_sequences
292 mhseq cur:-2
293 ],
294 [0],
295 [2
296 3
297 ])
298
299 MH_CHECK([mhseq: cur:n -- explicit dir (positive)],[mhseq25 cur:n cur:n+],[
300 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
301 echo "cur: 3" > Mail/inbox/.mh_sequences
302 mhseq cur:+2
303 ],
304 [0],
305 [3
306 4
307 ])
308
309 MH_CHECK([mhseq: next],[mhseq26 next],[
310 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
311 echo "cur: 3" > Mail/inbox/.mh_sequences
312 rm Mail/inbox/4
313 mhseq next
314 ],
315 [0],
316 [5
317 ])
318
319 MH_CHECK([mhseq: next:n -- default direction],[mhseq27 next:n],[
320 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
321 echo "cur: 3" > Mail/inbox/.mh_sequences
322 mhseq next:2
323 ],
324 [0],
325 [4
326 5
327 ])
328
329 MH_CHECK([mhseq: prev],[mhseq28 prev],[
330 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
331 echo "cur: 3" > Mail/inbox/.mh_sequences
332 rm Mail/inbox/2
333 mhseq prev
334 ],
335 [0],
336 [1
337 ])
338
339 MH_CHECK([mhseq: prev:n -- default direction],[mhseq29 prev:n],[
340 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
341 echo "cur: 3" > Mail/inbox/.mh_sequences
342 mhseq prev:2
343 ],
344 [0],
345 [1
346 2
347 ])
348
349 MH_CHECK([mhseq: first],[mhseq30 first],[
350 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
351 rm Mail/inbox/1
352 mhseq first
353 ],
354 [0],
355 [2
356 ])
357
358 MH_CHECK([mhseq: first:n -- default direction],[mhseq31 first:n],[
359 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
360 rm Mail/inbox/1
361 mhseq first:2
362 ],
363 [0],
364 [2
365 3
366 ])
367
368 MH_CHECK([mhseq: last],[mhseq32 last],[
369 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
370 mhseq last
371 ],
372 [0],
373 [5
374 ])
375
376 MH_CHECK([mhseq: last:n -- default direction],[mhseq33 last:n],[
377 MUT_MBCOPY($abs_top_srcdir/testsuite/mh/mbox1,[Mail/inbox])
378 mhseq last:2
379 ],
380 [0],
381 [4
382 5
383 ])
384
385 m4_popdef([MH_KEYWORDS])
386 # End of mhseq.at
...\ No newline at end of file ...\ No newline at end of file
...@@ -42,6 +42,7 @@ AT_CLEANUP ...@@ -42,6 +42,7 @@ AT_CLEANUP
42 AT_INIT 42 AT_INIT
43 43
44 m4_include([install-mh.at]) 44 m4_include([install-mh.at])
45 m4_include([mhseq.at])
45 m4_include([ali.at]) 46 m4_include([ali.at])
46 m4_include([folder.at]) 47 m4_include([folder.at])
47 m4_include([inc.at]) 48 m4_include([inc.at])
...@@ -56,3 +57,4 @@ m4_include([mhl.at]) ...@@ -56,3 +57,4 @@ m4_include([mhl.at])
56 m4_include([anno.at]) 57 m4_include([anno.at])
57 m4_include([pick.at]) 58 m4_include([pick.at])
58 m4_include([burst.at]) 59 m4_include([burst.at])
60 m4_include([comp.at])
......