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