]> diplodocus.org Git - nmh/blob - sbr/m_convert.c
sendsbr.c: Move interface to own file.
[nmh] / sbr / m_convert.c
1 /* m_convert.c -- parse a message range or sequence and set SELECTED
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "ssequal.h"
10 #include "m_convert.h"
11 #include "folder_realloc.h"
12 #include "context_find.h"
13 #include "seq_getnum.h"
14 #include "error.h"
15 #include "h/utils.h"
16
17 /*
18 * error codes for sequence
19 * and message range processing
20 */
21 #define BADMSG (-2)
22 #define BADRNG (-3)
23 #define BADNEW (-4)
24 #define BADNUM (-5)
25 #define BADLST (-6)
26
27 #define FIRST 1
28 #define LAST 2
29
30
31 #define getnew(mp) (mp->hghmsg + 1)
32
33 static int convdir; /* convert direction */
34 static char *delimp;
35
36 /*
37 * static prototypes
38 */
39 static int m_conv (struct msgs *, char *, int);
40 static int attr (struct msgs *, char *);
41
42
43 int
44 m_convert (struct msgs *mp, char *name)
45 {
46 int first, last, found, count, err;
47 char *bp, *cp;
48
49 /* check if user defined sequence */
50 err = attr (mp, cp = name);
51
52 if (err == -1)
53 return 0;
54 if (err < 0)
55 goto badmsg;
56 if (err > 0)
57 return 1;
58 /*
59 * else err == 0, so continue
60 */
61
62 found = 0;
63
64 /*
65 * Check for special "new" sequence, which
66 * is valid only if ALLOW_NEW is set.
67 */
68 if ((mp->msgflags & ALLOW_NEW) && !strcmp (cp, "new")) {
69 if ((err = first = getnew (mp)) <= 0)
70 goto badmsg;
71 goto single;
72 }
73
74 if (!strcmp (cp, "all"))
75 cp = "first-last";
76
77 if ((err = first = m_conv (mp, cp, FIRST)) <= 0)
78 goto badmsg;
79
80 cp = delimp;
81 if (*cp != '\0' && *cp != '-' && *cp != ':' && *cp != '=') {
82 badelim:
83 inform("illegal argument delimiter: `%c'(0%o)", *delimp, *delimp);
84 return 0;
85 }
86
87 if (*cp == '-') {
88 cp++;
89 if ((err = last = m_conv (mp, cp, LAST)) <= 0) {
90 badmsg:
91 switch (err) {
92 case BADMSG:
93 inform("no %s message", cp);
94 break;
95
96 case BADNUM:
97 inform("message %s doesn't exist", cp);
98 break;
99
100 case BADRNG:
101 inform("message %s out of range 1-%d", cp, mp->hghmsg);
102 break;
103
104 case BADLST:
105 badlist:
106 inform("bad message list %s", name);
107 break;
108
109 case BADNEW:
110 inform("folder full, no %s message", name);
111 break;
112
113 default:
114 inform("no messages match specification");
115 }
116 return 0;
117 }
118
119 if (last < first)
120 goto badlist;
121 if (*delimp)
122 goto badelim;
123 if (first > mp->hghmsg || last < mp->lowmsg) {
124 rangerr:
125 inform("no messages in range %s", name);
126 return 0;
127 }
128
129 /* tighten the range to search */
130 if (last > mp->hghmsg)
131 last = mp->hghmsg;
132 if (first < mp->lowmsg)
133 first = mp->lowmsg;
134
135 } else if (*cp == ':' || *cp == '=') {
136
137 bool is_range = *cp == ':';
138 cp++;
139
140 if (*cp == '-') {
141 /* foo:-3 or foo=-3 */
142 convdir = -1;
143 cp++;
144 } else if (*cp == '+') {
145 /* foo:+3 or foo=+3 is same as foo:3 or foo=3 */
146 convdir = 1;
147 cp++;
148 }
149 if ((count = atoi (bp = cp)) == 0)
150 goto badlist;
151 while (isdigit ((unsigned char) *bp))
152 bp++;
153 if (*bp)
154 goto badelim;
155 if ((convdir > 0 && first > mp->hghmsg)
156 || (convdir < 0 && first < mp->lowmsg))
157 goto rangerr;
158
159 /* tighten the range to search */
160 if (first < mp->lowmsg)
161 first = mp->lowmsg;
162 if (first > mp->hghmsg)
163 first = mp->hghmsg;
164
165 for (last = first;
166 last >= mp->lowmsg && last <= mp->hghmsg;
167 last += convdir)
168 if (does_exist (mp, last))
169 if (--count <= 0)
170 break;
171 if (is_range) { /* a range includes any messages that exist */
172 if (last < mp->lowmsg)
173 last = mp->lowmsg;
174 if (last > mp->hghmsg)
175 last = mp->hghmsg;
176 if (last < first) {
177 count = last;
178 last = first;
179 first = count;
180 }
181 } else { /* looking for the nth message. if not enough, fail. */
182 if (last < mp->lowmsg || last > mp->hghmsg) {
183 inform("no such message");
184 return 0;
185 }
186 first = last;
187 }
188 } else {
189
190 single:
191 /*
192 * Single Message
193 *
194 * If ALLOW_NEW is set, then allow selecting of an
195 * empty slot. If ALLOW_NEW is not set, then we
196 * check if message is in-range and exists.
197 */
198 if (mp->msgflags & ALLOW_NEW) {
199 /*
200 * We can get into a case where the "cur" sequence is way out
201 * of range, and because it's allowed to not exist (think
202 * of "rmm; next") it doesn't get checked to make sure it's
203 * within the range of messages in seq_init(). So if our
204 * desired sequence is out of range of the allocated folder
205 * limits simply reallocate the folder so it's within range.
206 */
207 if (first < mp->lowoff || first > mp->hghoff)
208 mp = folder_realloc(mp, min(first, mp->lowoff),
209 max(first, mp->hghoff));
210
211 set_select_empty (mp, first);
212 } else {
213 if (first > mp->hghmsg
214 || first < mp->lowmsg
215 || !(does_exist (mp, first))) {
216 if (!strcmp (name, "cur") || !strcmp (name, "."))
217 inform("no %s message", name);
218 else
219 inform("message %d doesn't exist", first);
220 return 0;
221 }
222 }
223 last = first; /* range of 1 */
224 }
225
226 /*
227 * Cycle through the range and select the messages
228 * that exist. If ALLOW_NEW is set, then we also check
229 * if we are selecting an empty slot.
230 */
231 for (; first <= last; first++) {
232 if (does_exist (mp, first) ||
233 ((mp->msgflags & ALLOW_NEW) && is_select_empty (mp, first))) {
234 if (!is_selected (mp, first)) {
235 set_selected (mp, first);
236 mp->numsel++;
237 if (mp->lowsel == 0 || first < mp->lowsel)
238 mp->lowsel = first;
239 if (first > mp->hghsel)
240 mp->hghsel = first;
241 }
242 found++;
243 }
244 }
245
246 if (!found)
247 goto rangerr;
248
249 return 1;
250 }
251
252 /*
253 * Convert the various message names to
254 * their numeric values.
255 *
256 * n (integer)
257 * prev
258 * next
259 * first
260 * last
261 * cur
262 * . (same as cur)
263 */
264
265 static int
266 m_conv (struct msgs *mp, char *str, int call)
267 {
268 int i;
269 char *cp, *bp;
270 char buf[16];
271
272 convdir = 1;
273 cp = bp = str;
274 if (isdigit ((unsigned char) *cp)) {
275 while (isdigit ((unsigned char) *bp))
276 bp++;
277 delimp = bp;
278 i = atoi (cp);
279
280 if (i <= mp->hghmsg)
281 return i;
282 if (*delimp || call == LAST)
283 return mp->hghmsg + 1;
284 if (mp->msgflags & ALLOW_NEW)
285 return BADRNG;
286 return BADNUM;
287 }
288
289 /* doesn't enforce lower case */
290 for (bp = buf; (isalpha((unsigned char) *cp) || *cp == '.')
291 && (bp - buf < (int) sizeof(buf) - 1); )
292 {
293 *bp++ = *cp++;
294 }
295 *bp++ = '\0';
296 delimp = cp;
297
298 if (!strcmp (buf, "first"))
299 return mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ?
300 mp->lowmsg : BADMSG;
301
302 if (!strcmp (buf, "last")) {
303 convdir = -1;
304 return mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ? mp->hghmsg : BADMSG;
305 }
306
307 if (!strcmp (buf, "cur") || !strcmp (buf, "."))
308 return mp->curmsg > 0 ? mp->curmsg : BADMSG;
309
310 if (!strcmp (buf, "prev")) {
311 convdir = -1;
312 for (i = (mp->curmsg <= mp->hghmsg) ? mp->curmsg - 1 : mp->hghmsg;
313 i >= mp->lowmsg; i--) {
314 if (does_exist (mp, i))
315 return i;
316 }
317 return BADMSG;
318 }
319
320 if (!strcmp (buf, "next")) {
321 for (i = (mp->curmsg >= mp->lowmsg) ? mp->curmsg + 1 : mp->lowmsg;
322 i <= mp->hghmsg; i++) {
323 if (does_exist (mp, i))
324 return i;
325 }
326 return BADMSG;
327 }
328
329 return BADLST;
330 }
331
332 /*
333 * Handle user defined sequences.
334 * They can take the following forms:
335 *
336 * seq
337 * seq:prev
338 * seq:next
339 * seq:first
340 * seq:last
341 * seq:+n
342 * seq:-n
343 * seq:n
344 */
345
346 static int
347 attr (struct msgs *mp, char *cp)
348 {
349 char *dp;
350 char *bp = NULL;
351 char *ep;
352 char op;
353 int i, j;
354 int found,
355 count = 0, /* range given? else use entire sequence */
356 first = 0,
357 start = 0;
358
359 /* hack for "cur-name", "cur-n", etc. */
360 if (!strcmp (cp, "cur"))
361 return 0;
362 if (has_prefix(cp, "cur")) {
363 if (cp[3] == ':' || cp[3] == '=')
364 return 0;
365 }
366
367 /* Check for sequence negation */
368 bool inverted = false;
369 if ((dp = context_find (nsequence)) && *dp != '\0' && ssequal (dp, cp)) {
370 inverted = true;
371 cp += strlen (dp);
372 }
373
374 convdir = 1; /* convert direction */
375
376 for (dp = cp; isalnum((unsigned char)*dp); dp++)
377 continue;
378
379 bool just_one = *dp == '='; /* want entire sequence or range */
380
381 if (*dp == ':') {
382 bp = dp++;
383 count = 1;
384
385 /*
386 * seq:prev (or)
387 * seq:next (or)
388 * seq:first (or)
389 * seq:last
390 */
391 if (isalpha ((unsigned char) *dp)) {
392 if (!strcmp (dp, "prev")) {
393 convdir = -1;
394 first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
395 ? mp->curmsg - 1
396 : mp->hghmsg;
397 start = first;
398 }
399 else if (!strcmp (dp, "next")) {
400 convdir = 1;
401 first = (mp->curmsg >= mp->lowmsg)
402 ? mp->curmsg + 1
403 : mp->lowmsg;
404 start = first;
405 }
406 else if (!strcmp (dp, "first")) {
407 convdir = 1;
408 start = mp->lowmsg;
409 }
410 else if (!strcmp (dp, "last")) {
411 convdir = -1;
412 start = mp->hghmsg;
413 }
414 else {
415 return BADLST;
416 }
417 } else {
418 /*
419 * seq:n (or)
420 * seq:+n (or)
421 * seq:-n
422 */
423 if (*dp == '+') {
424 /* foo:+3 is same as foo:3 */
425 dp++;
426 convdir = 1;
427 start = mp->lowmsg;
428 } else if (*dp == '-' || *dp == ':') {
429 /* foo:-3 or foo::3 */
430 dp++;
431 convdir = -1;
432 start = mp->hghmsg;
433 }
434 count = strtol(dp,&ep,10);
435 if (count == 0 || *ep) /* 0 illegal */
436 return BADLST;
437 }
438
439 op = *bp;
440 *bp = '\0'; /* temporarily terminate sequence name */
441 } else if (*dp == '=') {
442
443 bp = dp++;
444
445 if (*dp == '+') {
446 /* foo=+3 is same as foo=3 */
447 dp++;
448 convdir = 1;
449 start = mp->lowmsg;
450 } else if (*dp == '-') {
451 /* foo=-3 */
452 dp++;
453 convdir = -1;
454 start = mp->hghmsg;
455 }
456
457 count = strtol(dp,&ep,10); /* 0 illegal */
458 if (count == 0 || *ep)
459 return BADLST;
460
461 op = *bp;
462 *bp = '\0'; /* temporarily terminate sequence name */
463 }
464
465 i = seq_getnum (mp, cp); /* get index of sequence */
466
467 if (bp)
468 *bp = op; /* restore sequence name */
469 if (i == -1)
470 return 0;
471
472 found = 0; /* count the number we select for this argument */
473
474 if (!start)
475 start = mp->lowmsg;
476
477 for (j = start; j >= mp->lowmsg && j <= mp->hghmsg; j += convdir) {
478
479 if (does_exist (mp, j)
480 && inverted ? !in_sequence (mp, i, j) : in_sequence (mp, i, j)) {
481 found++;
482 /* we want all that we find, or just the last in the +/_ case */
483 if (!just_one || found >= count) {
484 if (!is_selected (mp, j)) {
485 set_selected (mp, j);
486 mp->numsel++;
487 if (mp->lowsel == 0 || j < mp->lowsel)
488 mp->lowsel = j;
489 if (j > mp->hghsel)
490 mp->hghsel = j;
491 }
492 }
493 /*
494 * If we have any sort of limit, then break out
495 * once we've found enough.
496 */
497 if (count && found >= count)
498 break;
499
500 }
501 }
502
503 if (mp->numsel > 0)
504 return mp->numsel;
505
506 if (first || just_one)
507 return BADMSG;
508 inform("sequence %s %s", cp, inverted ? "full" : "empty");
509 return -1;
510 }