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