]> diplodocus.org Git - nmh/blob - sbr/m_convert.c
Removed temporary probes added in commit
[nmh] / sbr / m_convert.c
1
2 /*
3 * m_convert.c -- parse a message range or sequence and set SELECTED
4 *
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 #include <h/mh.h>
11
12 /*
13 * error codes for sequence
14 * and message range processing
15 */
16 #define BADMSG (-2)
17 #define BADRNG (-3)
18 #define BADNEW (-4)
19 #define BADNUM (-5)
20 #define BADLST (-6)
21
22 #define FIRST 1
23 #define LAST 2
24
25
26 #define getnew(mp) (mp->hghmsg + 1)
27
28 static int convdir; /* convert direction */
29 static char *delimp;
30
31 /*
32 * static prototypes
33 */
34 static int m_conv (struct msgs *, char *, int);
35 static int attr (struct msgs *, char *);
36
37
38 int
39 m_convert (struct msgs *mp, char *name)
40 {
41 int first, last, found, count, is_range, err;
42 char *bp, *cp;
43
44 /* check if user defined sequence */
45 err = attr (mp, cp = name);
46
47 if (err == -1)
48 return 0;
49 else if (err < 0)
50 goto badmsg;
51 else if (err > 0)
52 return 1;
53 /*
54 * else err == 0, so continue
55 */
56
57 found = 0;
58 is_range = 1;
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 else
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 advise (NULL, "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 advise (NULL, "no %s message", cp);
91 break;
92
93 case BADNUM:
94 advise (NULL, "message %s doesn't exist", cp);
95 break;
96
97 case BADRNG:
98 advise (NULL, "message %s out of range 1-%d", cp, mp->hghmsg);
99 break;
100
101 case BADLST:
102 badlist:
103 advise (NULL, "bad message list %s", name);
104 break;
105
106 case BADNEW:
107 advise (NULL, "folder full, no %s message", name);
108 break;
109
110 default:
111 advise (NULL, "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 advise (NULL, "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 if (*cp == '=')
135 is_range = 0;
136
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 advise (NULL, "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, first < mp->lowoff ? first : mp->lowoff,
208 first > mp->hghoff ? 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 advise (NULL, "no %s message", name);
217 else
218 advise (NULL, "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 else if (*delimp || call == LAST)
282 return mp->hghmsg + 1;
283 else if (mp->msgflags & ALLOW_NEW)
284 return BADRNG;
285 else
286 return BADNUM;
287 }
288
289 #ifdef LOCALE
290 /* doesn't enforce lower case */
291 for (bp = buf; (isalpha((unsigned char) *cp) || *cp == '.')
292 && (bp - buf < (int) sizeof(buf) - 1); )
293 #else
294 for (bp = buf; ((*cp >= 'a' && *cp <= 'z') || *cp == '.')
295 && (bp - buf < (int) sizeof(buf) - 1); )
296 #endif /* LOCALE */
297 {
298 *bp++ = *cp++;
299 }
300 *bp++ = '\0';
301 delimp = cp;
302
303 if (!strcmp (buf, "first"))
304 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW)
305 ? mp->lowmsg : BADMSG);
306
307 if (!strcmp (buf, "last")) {
308 convdir = -1;
309 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ? mp->hghmsg : BADMSG);
310 }
311
312 if (!strcmp (buf, "cur") || !strcmp (buf, "."))
313 return (mp->curmsg > 0 ? mp->curmsg : BADMSG);
314
315 if (!strcmp (buf, "prev")) {
316 convdir = -1;
317 for (i = (mp->curmsg <= mp->hghmsg) ? mp->curmsg - 1 : mp->hghmsg;
318 i >= mp->lowmsg; i--) {
319 if (does_exist (mp, i))
320 return i;
321 }
322 return BADMSG;
323 }
324
325 if (!strcmp (buf, "next")) {
326 for (i = (mp->curmsg >= mp->lowmsg) ? mp->curmsg + 1 : mp->lowmsg;
327 i <= mp->hghmsg; i++) {
328 if (does_exist (mp, i))
329 return i;
330 }
331 return BADMSG;
332 }
333
334 return BADLST;
335 }
336
337 /*
338 * Handle user defined sequences.
339 * They can take the following forms:
340 *
341 * seq
342 * seq:prev
343 * seq:next
344 * seq:first
345 * seq:last
346 * seq:+n
347 * seq:-n
348 * seq:n
349 */
350
351 static int
352 attr (struct msgs *mp, char *cp)
353 {
354 char *dp;
355 char *bp = NULL;
356 char *ep;
357 char op;
358 int i, j;
359 int found,
360 inverted = 0,
361 count = 0, /* range given? else use entire sequence */
362 just_one = 0, /* want entire sequence or range */
363 first = 0,
364 start = 0;
365
366 /* hack for "cur-name", "cur-n", etc. */
367 if (!strcmp (cp, "cur"))
368 return 0;
369 if (strncmp ("cur", cp, 3) == 0) {
370 if (cp[3] == ':' || cp[3] == '=')
371 return 0;
372 }
373
374 /* Check for sequence negation */
375 if ((dp = context_find (nsequence)) && *dp != '\0' && ssequal (dp, cp)) {
376 inverted = 1;
377 cp += strlen (dp);
378 }
379
380 convdir = 1; /* convert direction */
381
382 for (dp = cp; *dp && isalnum((unsigned char) *dp); dp++)
383 continue;
384
385 if (*dp == ':') {
386 bp = dp++;
387 count = 1;
388
389 /*
390 * seq:prev (or)
391 * seq:next (or)
392 * seq:first (or)
393 * seq:last
394 */
395 if (isalpha ((unsigned char) *dp)) {
396 if (!strcmp (dp, "prev")) {
397 convdir = -1;
398 first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
399 ? mp->curmsg - 1
400 : mp->hghmsg;
401 start = first;
402 }
403 else if (!strcmp (dp, "next")) {
404 convdir = 1;
405 first = (mp->curmsg >= mp->lowmsg)
406 ? mp->curmsg + 1
407 : mp->lowmsg;
408 start = first;
409 }
410 else if (!strcmp (dp, "first")) {
411 convdir = 1;
412 start = mp->lowmsg;
413 }
414 else if (!strcmp (dp, "last")) {
415 convdir = -1;
416 start = mp->hghmsg;
417 }
418 else {
419 return BADLST;
420 }
421 } else {
422 /*
423 * seq:n (or)
424 * seq:+n (or)
425 * seq:-n
426 */
427 if (*dp == '+') {
428 /* foo:+3 is same as foo:3 */
429 dp++;
430 convdir = 1;
431 start = mp->lowmsg;
432 } else if (*dp == '-' || *dp == ':') {
433 /* foo:-3 or foo::3 */
434 dp++;
435 convdir = -1;
436 start = mp->hghmsg;
437 }
438 count = strtol(dp,&ep,10);
439 if (count == 0 || *ep) /* 0 illegal */
440 return BADLST;
441 }
442
443 op = *bp;
444 *bp = '\0'; /* temporarily terminate sequence name */
445 } else if (*dp == '=') {
446
447 bp = dp++;
448
449 if (*dp == '+') {
450 /* foo=+3 is same as foo=3 */
451 dp++;
452 convdir = 1;
453 start = mp->lowmsg;
454 } else if (*dp == '-') {
455 /* foo=-3 */
456 dp++;
457 convdir = -1;
458 start = mp->hghmsg;
459 }
460
461 count = strtol(dp,&ep,10); /* 0 illegal */
462 if (count == 0 || *ep)
463 return BADLST;
464
465 just_one = 1;
466
467 op = *bp;
468 *bp = '\0'; /* temporarily terminate sequence name */
469 }
470
471 i = seq_getnum (mp, cp); /* get index of sequence */
472
473 if (bp)
474 *bp = op; /* restore sequence name */
475 if (i == -1)
476 return 0;
477
478 found = 0; /* count the number we select for this argument */
479
480 if (!start)
481 start = mp->lowmsg;
482
483 for (j = start; j >= mp->lowmsg && j <= mp->hghmsg; j += convdir) {
484
485 if (does_exist (mp, j)
486 && inverted ? !in_sequence (mp, i, j) : in_sequence (mp, i, j)) {
487 found++;
488 /* we want all that we find, or just the last in the +/_ case */
489 if (!just_one || found >= count) {
490 if (!is_selected (mp, j)) {
491 set_selected (mp, j);
492 mp->numsel++;
493 if (mp->lowsel == 0 || j < mp->lowsel)
494 mp->lowsel = j;
495 if (j > mp->hghsel)
496 mp->hghsel = j;
497 }
498 }
499 /*
500 * If we have any sort of limit, then break out
501 * once we've found enough.
502 */
503 if (count && found >= count)
504 break;
505
506 }
507 }
508
509 if (mp->numsel > 0)
510 return mp->numsel;
511
512 if (first || just_one)
513 return BADMSG;
514 advise (NULL, "sequence %s %s", cp, inverted ? "full" : "empty");
515 return -1;
516 }