Various minor cleanups in sbr/m_convert.c.
[mmh] / sbr / m_convert.c
1 /*
2 ** m_convert.c -- parse a message range or sequence and set SELECTED
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9
10 /* FIXME: This code needs rework! Rewrite as a parser? */
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 getbeyond(mp) ((mp)->hghmsg + 1)
28
29 static int convdir;  /* convert direction */
30 static char *delimp;  /* delimiter pointer */
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         unsigned char *bp;
44         char *cp;
45
46         /* check if user defined sequence */
47         err = attr(mp, cp = name);
48         if (err == -1)
49                 return 0;
50         else if (err < 0)
51                 goto badmsg;
52         else if (err > 0) {
53                 /* it had been a user-defined sequence: we're finished */
54                 return 1;
55         }
56         /*
57         ** else err == 0, so continue
58         ** (we know here: it is no user-defined seq)
59         */
60
61         found = 0;
62
63         /*
64         ** Check for special beyond sequence, which
65         ** is valid only if ALLOW_BEYOND is set.
66         ** (It can appear only on its own.)
67         */
68         if ((mp->msgflags & ALLOW_BEYOND) && !strcmp(cp, seq_beyond)) {
69                 if ((err = first = getbeyond(mp)) <= 0)
70                         goto badmsg;
71                 else
72                         goto single;
73         }
74
75         /* replace `a' with `f-l' */
76         if (!strcmp(cp, seq_all))
77                 cp = concat(seq_first, "-", seq_last, NULL);
78
79         if ((err = first = m_conv(mp, cp, FIRST)) <= 0)
80                 goto badmsg;
81
82         cp = delimp;
83         switch (*cp) {
84         case '-':
85                 /* global range */
86                 cp++;
87                 if ((err = last = m_conv(mp, cp, LAST)) <= 0) {
88 badmsg:
89                         switch (err) {
90                         case BADMSG:
91                                 advise(NULL, "no %s message", cp);
92                                 break;
93
94                         case BADNUM:
95                                 advise(NULL, "message %s doesn't exist", cp);
96                                 break;
97
98                         case BADRNG:
99                                 advise(NULL, "message %s out of range 1-%d",
100                                                 cp, mp->hghmsg);
101                                 break;
102
103                         case BADLST:
104 badlist:
105                                 advise(NULL, "bad message list %s", name);
106                                 break;
107
108                         case BADNEW:
109                                 advise(NULL, "folder full, no %s message",
110                                                 name);
111                                 break;
112
113                         default:
114                                 advise(NULL, "no messages match specification");
115                         }
116                         return 0;
117                 }
118
119                 if (last < first)
120                         goto badlist;
121                 if (*delimp)
122                         goto badelim;
123                 if (first > mp->hghmsg || last < mp->lowmsg) {
124 rangerr:
125                         advise(NULL, "no messages in range %s", name);
126                         return 0;
127                 }
128
129                 /* tighten the range to search */
130                 if (last > mp->hghmsg)
131                         last = mp->hghmsg;
132                 if (first < mp->lowmsg)
133                         first = mp->lowmsg;
134                 break;
135
136         case ':':
137                 /* anchored range */
138                 cp++;
139                 if (*cp == '-') {
140                         convdir = -1;
141                         cp++;
142                 } else if (*cp == '+') {
143                         convdir = 1;
144                         cp++;
145                 }
146                 if ((range = atoi(bp = cp)) == 0)
147                         goto badlist;
148                 while (isdigit(*bp))
149                         bp++;
150                 if (*bp)
151                         goto badelim;
152                 if ((convdir > 0 && first > mp->hghmsg)
153                                 || (convdir < 0 && first < mp->lowmsg))
154                         goto rangerr;
155
156                 /* tighten the range to search */
157                 if (first < mp->lowmsg)
158                         first = mp->lowmsg;
159                 if (first > mp->hghmsg)
160                         first = mp->hghmsg;
161
162                 for (last = first; last >= mp->lowmsg && last <= mp->hghmsg;
163                         last += convdir)
164                         if (does_exist(mp, last) && (--range <= 0))
165                                 break;
166                 if (last < mp->lowmsg)
167                         last = mp->lowmsg;
168                 if (last > mp->hghmsg)
169                         last = mp->hghmsg;
170                 if (last < first) {
171                         range = last;
172                         last = first;
173                         first = range;
174                 }
175                 break;
176
177         case '\0':
178                 /* single name */
179 single:
180                 /*
181                 ** AFAICS, the only cases to reach here are:
182                 **     f, l, p, n, c, b
183                 ** But I'm not sure.  --meillo
184                 */
185                 /*
186                 ** Single Message
187                 **
188                 ** If ALLOW_BEYOND is set, then allow selecting of an
189                 ** empty slot.  If ALLOW_BEYOND is not set, then we
190                 ** check if message is in-range and exists.
191                 */
192                 if (mp->msgflags & ALLOW_BEYOND) {
193                         set_select_empty(mp, first);
194                 } else if (first > mp->hghmsg || first < mp->lowmsg
195                                 || !does_exist(mp, first)) {
196                         if (!strcmp(name, seq_cur))
197                                 advise(NULL, "no current message");
198                         else
199                                 advise(NULL, "message %d doesn't exist",
200                                                 first);
201                         return 0;
202                 }
203                 last = first;  /* range of 1 */
204                 break;
205
206         default:
207 badelim:
208                 advise(NULL, "illegal argument delimiter: `%c'(0%o)",
209                                 *delimp, *delimp);
210                 return 0;
211                 break;
212         }
213
214         /*
215         ** Cycle through the range and select the messages
216         ** that exist.  If ALLOW_BEYOND is set, then we also check
217         ** if we are selecting an empty slot.
218         */
219         for (; first <= last; first++) {
220                 if (does_exist(mp, first) || (
221                                 (mp->msgflags & ALLOW_BEYOND) &&
222                                 is_select_empty(mp, first))) {
223                         if (!is_selected(mp, first)) {
224                                 set_selected(mp, first);
225                                 mp->numsel++;
226                                 if (mp->lowsel == 0 || first < mp->lowsel)
227                                         mp->lowsel = first;
228                                 if (first > mp->hghsel)
229                                         mp->hghsel = first;
230                         }
231                         found++;
232                 }
233         }
234
235         if (!found)
236                 goto rangerr;
237
238         return 1;
239 }
240
241 /*
242 ** Convert the various message names to their numeric values.
243 **
244 ** 42  (any integer)
245 ** p
246 ** n
247 ** f
248 ** l
249 ** c
250 */
251 static int
252 m_conv(struct msgs *mp, char *str, int call)
253 {
254         register int i;
255         register unsigned char *cp, *bp;
256         unsigned char buf[16];
257
258         convdir = 1;
259         cp = bp = str;
260         if (isdigit(*cp)) {
261                 while (isdigit(*bp))
262                         bp++;
263                 delimp = bp;
264                 i = atoi(cp);
265
266                 if (i <= mp->hghmsg)
267                         return i;
268                 else if (*delimp || call == LAST)
269                         return mp->hghmsg + 1;
270                 else if (mp->msgflags & ALLOW_BEYOND)
271                         return BADRNG;
272                 else
273                         return BADNUM;
274         }
275
276 #ifdef LOCALE
277         /* doesn't enforce lower case */
278         for (bp = buf; isalpha(*cp) && (bp - buf < sizeof(buf) - 1); )
279 #else
280         for (bp = buf; islower(*cp) && (bp - buf < sizeof(buf) - 1); )
281 #endif /* LOCALE */
282         {
283                 *bp++ = *cp++;
284         }
285         *bp++ = '\0';
286         delimp = cp;
287
288         if (!strcmp(buf, seq_first))
289                 return (mp->hghmsg || !(mp->msgflags & ALLOW_BEYOND)
290                                 ? mp->lowmsg : BADMSG);
291
292         if (!strcmp(buf, seq_last)) {
293                 convdir = -1;
294                 return (mp->hghmsg || !(mp->msgflags & ALLOW_BEYOND) ? mp->hghmsg : BADMSG);
295         }
296
297         if (!strcmp(buf, seq_cur))
298                 return (mp->curmsg > 0 ? mp->curmsg : BADMSG);
299
300         if (!strcmp(buf, seq_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, seq_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:p
328 ** seq:n
329 ** seq:f
330 ** seq:l
331 ** seq:+42
332 ** seq:-42
333 ** seq:42
334 **
335 ** (`42' being an arbitrary integer)
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         int inverted = 0;
345         int range = 0;  /* no range */
346         int first = 0;
347
348         /* hack for "c-..." */
349         if (!strcmp(cp, seq_cur))
350                 return 0;
351         /* "c:..." -- this code need to be rewritten... */
352         if (strncmp(seq_cur, cp, strlen(seq_cur))==0 &&
353                         cp[strlen(seq_cur)] == ':') {
354                 return 0;
355         }
356
357         /* Check for sequence negation */
358         if (!(dp = context_find(nsequence))) {
359                 dp = seq_neg;  /* use default */
360         }
361         if (dp && *dp && isprefix(dp, cp)) {
362                 inverted = 1;
363                 cp += strlen(dp);
364         }
365
366         convdir = 1;  /* convert direction */
367
368         for (dp = cp; *dp && isalnum(*dp); dp++)
369                 continue;
370
371         if (*dp == ':') {
372                 bp = dp++;
373                 range = 1;
374
375                 /*
376                 ** seq:p  (or)
377                 ** seq:n  (or)
378                 ** seq:f  (or)
379                 ** seq:l
380                 */
381                 if (isalpha(*dp)) {
382                         if (!strcmp(dp, seq_prev)) {
383                                 convdir = -1;
384                                 first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
385                                         ? mp->curmsg - 1 : mp->hghmsg;
386                         } else if (!strcmp(dp, seq_next)) {
387                                 convdir = 1;
388                                 first = (mp->curmsg >= mp->lowmsg)
389                                         ? mp->curmsg + 1 : mp->lowmsg;
390                         } else if (!strcmp(dp, seq_first)) {
391                                 convdir = 1;
392                         } else if (!strcmp(dp, seq_last)) {
393                                 convdir = -1;
394                         } else
395                                 return BADLST;
396                 } else {
397                         /*
398                         ** seq:42  (or)
399                         ** seq:+42 (or)
400                         ** seq:-42
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 }