]> diplodocus.org Git - nmh/blob - sbr/m_convert.c
Fix stupid accidental dependence on a bash quirk in previous
[nmh] / sbr / m_convert.c
1
2 /*
3 * m_convert.c -- parse a message range or sequence and set SELECTED
4 *
5 * $Id$
6 *
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
10 */
11
12 #include <h/mh.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 #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, range, 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 else if (err < 0)
51 goto badmsg;
52 else 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 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 != ':') {
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 == ':') {
133 cp++;
134 if (*cp == '-') {
135 convdir = -1;
136 cp++;
137 } else {
138 if (*cp == '+') {
139 convdir = 1;
140 cp++;
141 }
142 }
143 if ((range = atoi (bp = cp)) == 0)
144 goto badlist;
145 while (isdigit (*bp))
146 bp++;
147 if (*bp)
148 goto badelim;
149 if ((convdir > 0 && first > mp->hghmsg)
150 || (convdir < 0 && first < mp->lowmsg))
151 goto rangerr;
152
153 /* tighten the range to search */
154 if (first < mp->lowmsg)
155 first = mp->lowmsg;
156 if (first > mp->hghmsg)
157 first = mp->hghmsg;
158
159 for (last = first;
160 last >= mp->lowmsg && last <= mp->hghmsg;
161 last += convdir)
162 if (does_exist (mp, last))
163 if (--range <= 0)
164 break;
165 if (last < mp->lowmsg)
166 last = mp->lowmsg;
167 if (last > mp->hghmsg)
168 last = mp->hghmsg;
169 if (last < first) {
170 range = last;
171 last = first;
172 first = range;
173 }
174 } else {
175
176 single:
177 /*
178 * Single Message
179 *
180 * If ALLOW_NEW is set, then allow selecting of an
181 * empty slot. If ALLOW_NEW is not set, then we
182 * check if message is in-range and exists.
183 */
184 if (mp->msgflags & ALLOW_NEW) {
185 set_select_empty (mp, first);
186 } else {
187 if (first > mp->hghmsg
188 || first < mp->lowmsg
189 || !(does_exist (mp, first))) {
190 if (!strcmp (name, "cur") || !strcmp (name, "."))
191 advise (NULL, "no %s message", name);
192 else
193 advise (NULL, "message %d doesn't exist", first);
194 return 0;
195 }
196 }
197 last = first; /* range of 1 */
198 }
199
200 /*
201 * Cycle through the range and select the messages
202 * that exist. If ALLOW_NEW is set, then we also check
203 * if we are selecting an empty slot.
204 */
205 for (; first <= last; first++) {
206 if (does_exist (mp, first) ||
207 ((mp->msgflags & ALLOW_NEW) && is_select_empty (mp, first))) {
208 if (!is_selected (mp, first)) {
209 set_selected (mp, first);
210 mp->numsel++;
211 if (mp->lowsel == 0 || first < mp->lowsel)
212 mp->lowsel = first;
213 if (first > mp->hghsel)
214 mp->hghsel = first;
215 }
216 found++;
217 }
218 }
219
220 if (!found)
221 goto rangerr;
222
223 return 1;
224 }
225
226 /*
227 * Convert the various message names to
228 * their numeric values.
229 *
230 * n (integer)
231 * prev
232 * next
233 * first
234 * last
235 * cur
236 * . (same as cur)
237 */
238
239 static int
240 m_conv (struct msgs *mp, char *str, int call)
241 {
242 register int i;
243 register char *cp, *bp;
244 char buf[16];
245
246 convdir = 1;
247 cp = bp = str;
248 if (isdigit (*cp)) {
249 while (isdigit (*bp))
250 bp++;
251 delimp = bp;
252 i = atoi (cp);
253
254 if (i <= mp->hghmsg)
255 return i;
256 else if (*delimp || call == LAST)
257 return mp->hghmsg + 1;
258 else if (mp->msgflags & ALLOW_NEW)
259 return BADRNG;
260 else
261 return BADNUM;
262 }
263
264 #ifdef LOCALE
265 /* doesn't enforce lower case */
266 for (bp = buf; (isalpha(*cp) || *cp == '.')
267 && (bp - buf < sizeof(buf) - 1); )
268 #else
269 for (bp = buf; ((*cp >= 'a' && *cp <= 'z') || *cp == '.')
270 && (bp - buf < sizeof(buf) - 1); )
271 #endif /* LOCALE */
272 {
273 *bp++ = *cp++;
274 }
275 *bp++ = '\0';
276 delimp = cp;
277
278 if (!strcmp (buf, "first"))
279 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW)
280 ? mp->lowmsg : BADMSG);
281
282 if (!strcmp (buf, "last")) {
283 convdir = -1;
284 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ? mp->hghmsg : BADMSG);
285 }
286
287 if (!strcmp (buf, "cur") || !strcmp (buf, "."))
288 return (mp->curmsg > 0 ? mp->curmsg : BADMSG);
289
290 if (!strcmp (buf, "prev")) {
291 convdir = -1;
292 for (i = (mp->curmsg <= mp->hghmsg) ? mp->curmsg - 1 : mp->hghmsg;
293 i >= mp->lowmsg; i--) {
294 if (does_exist (mp, i))
295 return i;
296 }
297 return BADMSG;
298 }
299
300 if (!strcmp (buf, "next")) {
301 for (i = (mp->curmsg >= mp->lowmsg) ? mp->curmsg + 1 : mp->lowmsg;
302 i <= mp->hghmsg; i++) {
303 if (does_exist (mp, i))
304 return i;
305 }
306 return BADMSG;
307 }
308
309 return BADLST;
310 }
311
312 /*
313 * Handle user defined sequences.
314 * They can take the following forms:
315 *
316 * seq
317 * seq:prev
318 * seq:next
319 * seq:first
320 * seq:last
321 * seq:+n
322 * seq:-n
323 * seq:n
324 */
325
326 static int
327 attr (struct msgs *mp, char *cp)
328 {
329 register char *dp;
330 char *bp = NULL;
331 register int i, j;
332 int found,
333 inverted = 0,
334 range = 0, /* no range */
335 first = 0;
336
337 /* hack for "cur-name", "cur-n", etc. */
338 if (!strcmp (cp, "cur"))
339 return 0;
340 if (ssequal ("cur:", cp)) /* this code need to be rewritten... */
341 return 0;
342
343 /* Check for sequence negation */
344 if ((dp = context_find (nsequence)) && *dp != '\0' && ssequal (dp, cp)) {
345 inverted = 1;
346 cp += strlen (dp);
347 }
348
349 convdir = 1; /* convert direction */
350
351 for (dp = cp; *dp && isalnum(*dp); dp++)
352 continue;
353
354 if (*dp == ':') {
355 bp = dp++;
356 range = 1;
357
358 /*
359 * seq:prev (or)
360 * seq:next (or)
361 * seq:first (or)
362 * seq:last
363 */
364 if (isalpha (*dp)) {
365 if (!strcmp (dp, "prev")) {
366 convdir = -1;
367 first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
368 ? mp->curmsg - 1
369 : mp->hghmsg;
370 }
371 else if (!strcmp (dp, "next")) {
372 convdir = 1;
373 first = (mp->curmsg >= mp->lowmsg)
374 ? mp->curmsg + 1
375 : mp->lowmsg;
376 }
377 else if (!strcmp (dp, "first")) {
378 convdir = 1;
379 }
380 else if (!strcmp (dp, "last")) {
381 convdir = -1;
382 }
383 else
384 return BADLST;
385 } else {
386 /*
387 * seq:n (or)
388 * seq:+n (or)
389 * seq:-n
390 */
391 if (*dp == '+')
392 dp++;
393 else if (*dp == '-') {
394 dp++;
395 convdir = -1;
396 }
397 if ((range = atoi(dp)) == 0)
398 return BADLST;
399 while (isdigit (*dp))
400 dp++;
401 if (*dp)
402 return BADLST;
403 }
404
405 *bp = '\0'; /* temporarily terminate sequence name */
406 }
407
408 i = seq_getnum (mp, cp); /* get index of sequence */
409
410 if (bp)
411 *bp = ':'; /* restore sequence name */
412 if (i == -1)
413 return 0;
414
415 found = 0; /* count the number we select for this argument */
416
417 for (j = first ? first : (convdir > 0) ? mp->lowmsg : mp->hghmsg;
418 j >= mp->lowmsg && j <= mp->hghmsg; j += convdir) {
419 if (does_exist (mp, j)
420 && inverted ? !in_sequence (mp, i, j) : in_sequence (mp, i, j)) {
421 if (!is_selected (mp, j)) {
422 set_selected (mp, j);
423 mp->numsel++;
424 if (mp->lowsel == 0 || j < mp->lowsel)
425 mp->lowsel = j;
426 if (j > mp->hghsel)
427 mp->hghsel = j;
428 }
429 found++;
430
431 /*
432 * If we have a range, then break out
433 * once we've found enough.
434 */
435 if (range && found >= range)
436 break;
437 }
438 }
439
440 if (found > 0)
441 return found;
442
443 if (first)
444 return BADMSG;
445 advise (NULL, "sequence %s %s", cp, inverted ? "full" : "empty");
446 return -1;
447 }