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