Renamed all standard sequences (e.g. cur->c) and made them globally changeable
[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 #include <h/mh.h>
10
11 /*
12 ** error codes for sequence
13 ** and message range processing
14 */
15 #define BADMSG (-2)
16 #define BADRNG (-3)
17 #define BADNEW (-4)
18 #define BADNUM (-5)
19 #define BADLST (-6)
20
21 #define FIRST 1
22 #define LAST 2
23
24 #define getnew(mp) ((mp)->hghmsg + 1)
25
26 static int convdir;  /* convert direction */
27 static char *delimp;  /* delimiter pointer */
28
29 /*
30 ** static prototypes
31 */
32 static int m_conv(struct msgs *, char *, int);
33 static int attr(struct msgs *, char *);
34
35
36 int
37 m_convert(struct msgs *mp, char *name)
38 {
39         int first, last, found, range, err;
40         unsigned char *bp;
41         char *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 beyond sequence, which
60         ** is valid only if ALLOW_NEW is set.
61         */
62         if ((mp->msgflags & ALLOW_NEW) && !strcmp(cp, seq_beyond)) {
63                 if ((err = first = getnew(mp)) <= 0)
64                         goto badmsg;
65                 else
66                         goto single;
67         }
68
69         if (!strcmp(cp, seq_all))
70                 cp = concat(seq_first, "-", seq_last, NULL);
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)",
79                                 *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",
98                                                 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",
108                                                 name);
109                                 break;
110
111                         default:
112                                 advise(NULL, "no messages match specification");
113                         }
114                         return 0;
115                 }
116
117                 if (last < first)
118                         goto badlist;
119                 if (*delimp)
120                         goto badelim;
121                 if (first > mp->hghmsg || last < mp->lowmsg) {
122 rangerr:
123                         advise(NULL, "no messages in range %s", name);
124                         return 0;
125                 }
126
127                 /* tighten the range to search */
128                 if (last > mp->hghmsg)
129                         last = mp->hghmsg;
130                 if (first < mp->lowmsg)
131                         first = mp->lowmsg;
132
133         } else if (*cp == ':') {
134                 cp++;
135                 if (*cp == '-') {
136                         convdir = -1;
137                         cp++;
138                 } else if (*cp == '+') {
139                         convdir = 1;
140                         cp++;
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; 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                         set_select_empty(mp, first);
184                 } else if (first > mp->hghmsg || first < mp->lowmsg
185                                 || !does_exist(mp, first)) {
186                         if (!strcmp(name, seq_cur))
187                                 advise(NULL, "no %s message", name);
188                         else
189                                 advise(NULL, "message %d doesn't exist",
190                                                 first);
191                         return 0;
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 ** their numeric values.
225 ** (`42' being an arbitrary number)
226 **
227 ** 42
228 ** prev
229 ** next
230 ** first
231 ** last
232 ** cur
233 */
234 static int
235 m_conv(struct msgs *mp, char *str, int call)
236 {
237         register int i;
238         register unsigned char *cp, *bp;
239         unsigned char buf[16];
240
241         convdir = 1;
242         cp = bp = str;
243         if (isdigit(*cp)) {
244                 while (isdigit(*bp))
245                         bp++;
246                 delimp = bp;
247                 i = atoi(cp);
248
249                 if (i <= mp->hghmsg)
250                         return i;
251                 else if (*delimp || call == LAST)
252                         return mp->hghmsg + 1;
253                 else if (mp->msgflags & ALLOW_NEW)
254                         return BADRNG;
255                 else
256                         return BADNUM;
257         }
258
259 #ifdef LOCALE
260         /* doesn't enforce lower case */
261         for (bp = buf; isalpha(*cp) && (bp - buf < sizeof(buf) - 1); )
262 #else
263         for (bp = buf; islower(*cp) && (bp - buf < sizeof(buf) - 1); )
264 #endif /* LOCALE */
265         {
266                 *bp++ = *cp++;
267         }
268         *bp++ = '\0';
269         delimp = cp;
270
271         if (!strcmp(buf, seq_first))
272                 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW)
273                                 ? mp->lowmsg : BADMSG);
274
275         if (!strcmp(buf, seq_last)) {
276                 convdir = -1;
277                 return (mp->hghmsg || !(mp->msgflags & ALLOW_NEW) ? mp->hghmsg : BADMSG);
278         }
279
280         if (!strcmp(buf, seq_cur))
281                 return (mp->curmsg > 0 ? mp->curmsg : BADMSG);
282
283         if (!strcmp(buf, seq_prev)) {
284                 convdir = -1;
285                 for (i = (mp->curmsg <= mp->hghmsg) ? mp->curmsg - 1 : mp->hghmsg;
286                         i >= mp->lowmsg; i--) {
287                         if (does_exist(mp, i))
288                                 return i;
289                 }
290                 return BADMSG;
291         }
292
293         if (!strcmp(buf, seq_next)) {
294                 for (i = (mp->curmsg >= mp->lowmsg) ? mp->curmsg + 1 : mp->lowmsg;
295                         i <= mp->hghmsg; i++) {
296                         if (does_exist(mp, i))
297                                 return i;
298                 }
299                 return BADMSG;
300         }
301
302         return BADLST;
303 }
304
305 /*
306 ** Handle user defined sequences.
307 ** They can take the following forms:
308 ** (`42' being an arbitrary number)
309 **
310 ** seq
311 ** seq:prev
312 ** seq:next
313 ** seq:first
314 ** seq:last
315 ** seq:+42
316 ** seq:-42
317 ** seq:42
318 */
319 static int
320 attr(struct msgs *mp, char *cp)
321 {
322         register unsigned char *dp;
323         char *bp = NULL;
324         register int i, j;
325         int found;
326         int inverted = 0;
327         int range = 0;  /* no range */
328         int first = 0;
329
330         /* hack for "c-name", "c-42", etc. */
331         if (!strcmp(cp, seq_cur))
332                 return 0;
333         /* "c:..." -- this code need to be rewritten... */
334         if (strncmp(seq_cur, cp, strlen(seq_cur))==0 &&
335                         cp[strlen(seq_cur)] == ':') {
336                 return 0;
337         }
338
339         /* Check for sequence negation */
340         if (!(dp = context_find(nsequence))) {
341                 dp = seq_neg;  /* use default */
342         }
343         if (dp && *dp && isprefix(dp, cp)) {
344                 inverted = 1;
345                 cp += strlen(dp);
346         }
347
348         convdir = 1;  /* convert direction */
349
350         for (dp = cp; *dp && isalnum(*dp); dp++)
351                 continue;
352
353         if (*dp == ':') {
354                 bp = dp++;
355                 range = 1;
356
357                 /*
358                 ** seq:prev  (or)
359                 ** seq:next  (or)
360                 ** seq:first (or)
361                 ** seq:last
362                 */
363                 if (isalpha(*dp)) {
364                         if (!strcmp(dp, seq_prev)) {
365                                 convdir = -1;
366                                 first = (mp->curmsg > 0) && (mp->curmsg <= mp->hghmsg)
367                                         ? mp->curmsg - 1 : mp->hghmsg;
368                         } else if (!strcmp(dp, seq_next)) {
369                                 convdir = 1;
370                                 first = (mp->curmsg >= mp->lowmsg)
371                                         ? mp->curmsg + 1 : mp->lowmsg;
372                         } else if (!strcmp(dp, seq_first)) {
373                                 convdir = 1;
374                         } else if (!strcmp(dp, seq_last)) {
375                                 convdir = -1;
376                         } else
377                                 return BADLST;
378                 } else {
379                         /*
380                         ** seq:42  (or)
381                         ** seq:+42 (or)
382                         ** seq:-42
383                         */
384                         if (*dp == '+')
385                                 dp++;
386                         else if (*dp == '-') {
387                                 dp++;
388                                 convdir = -1;
389                         }
390                         if ((range = atoi(dp)) == 0)
391                                 return BADLST;
392                         while (isdigit(*dp))
393                                 dp++;
394                         if (*dp)
395                                 return BADLST;
396                 }
397
398                 *bp = '\0';  /* temporarily terminate sequence name */
399         }
400
401         i = seq_getnum(mp, cp);  /* get index of sequence */
402
403         if (bp)
404                 *bp = ':';  /* restore sequence name */
405         if (i == -1)
406                 return 0;
407
408         found = 0;  /* count the number we select for this argument */
409
410         for (j = first ? first : (convdir > 0) ? mp->lowmsg : mp->hghmsg;
411                 j >= mp->lowmsg && j <= mp->hghmsg; j += convdir) {
412                 if (does_exist(mp, j)
413                         && inverted ? !in_sequence(mp, i, j) : in_sequence(mp, i, j)) {
414                         if (!is_selected(mp, j)) {
415                                 set_selected(mp, j);
416                                 mp->numsel++;
417                                 if (mp->lowsel == 0 || j < mp->lowsel)
418                                         mp->lowsel = j;
419                                 if (j > mp->hghsel)
420                                         mp->hghsel = j;
421                         }
422                         found++;
423
424                         /*
425                         ** If we have a range, then break out
426                         ** once we've found enough.
427                         */
428                         if (range && found >= range)
429                                 break;
430                 }
431         }
432
433         if (found > 0)
434                 return found;
435
436         if (first)
437                 return BADMSG;
438         advise(NULL, "sequence %s %s", cp, inverted ? "full" : "empty");
439         return -1;
440 }