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