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