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