NeoMutt  2023-05-17-33-gce4425
Teaching an old dog new tricks
DOXYGEN
sequence.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <limits.h>
31#include <stdio.h>
32#include <string.h>
33#include <sys/stat.h>
34#include <unistd.h>
35#include "private.h"
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "email/lib.h"
39#include "core/lib.h"
40#include "sequence.h"
41
49static void mh_seq_alloc(struct MhSequences *mhs, int i)
50{
51 if ((i <= mhs->max) && mhs->flags)
52 return;
53
54 const int newmax = i + 128;
55 int j = mhs->flags ? mhs->max + 1 : 0;
56 mutt_mem_realloc(&mhs->flags, sizeof(mhs->flags[0]) * (newmax + 1));
57 while (j <= newmax)
58 mhs->flags[j++] = 0;
59
60 mhs->max = newmax;
61}
62
67void mh_seq_free(struct MhSequences *mhs)
68{
69 FREE(&mhs->flags);
70}
71
79{
80 if (!mhs->flags || (i > mhs->max))
81 return 0;
82 return mhs->flags[i];
83}
84
92static MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
93{
94 mh_seq_alloc(mhs, i);
95 mhs->flags[i] |= f;
96 return mhs->flags[i];
97}
98
107void mh_seq_add_one(struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
108{
109 bool unseen_done = false;
110 bool flagged_done = false;
111 bool replied_done = false;
112
113 char *tmpfname = NULL;
114 char sequences[PATH_MAX] = { 0 };
115
116 char seq_unseen[256] = { 0 };
117 char seq_replied[256] = { 0 };
118 char seq_flagged[256] = { 0 };
119
120 char *buf = NULL;
121 size_t sz;
122
123 FILE *fp_new = NULL;
124 if (!mh_mkstemp(m, &fp_new, &tmpfname))
125 return;
126
127 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
128 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
129 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
130 snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
131 snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
132 snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
133
134 snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
135 FILE *fp_old = fopen(sequences, "r");
136 if (fp_old)
137 {
138 while ((buf = mutt_file_read_line(buf, &sz, fp_old, NULL, MUTT_RL_NO_FLAGS)))
139 {
140 if (unseen && mutt_strn_equal(buf, seq_unseen, mutt_str_len(seq_unseen)))
141 {
142 fprintf(fp_new, "%s %d\n", buf, n);
143 unseen_done = true;
144 }
145 else if (flagged && mutt_strn_equal(buf, seq_flagged, mutt_str_len(seq_flagged)))
146 {
147 fprintf(fp_new, "%s %d\n", buf, n);
148 flagged_done = true;
149 }
150 else if (replied && mutt_strn_equal(buf, seq_replied, mutt_str_len(seq_replied)))
151 {
152 fprintf(fp_new, "%s %d\n", buf, n);
153 replied_done = true;
154 }
155 else
156 {
157 fprintf(fp_new, "%s\n", buf);
158 }
159 }
160 }
161 mutt_file_fclose(&fp_old);
162 FREE(&buf);
163
164 if (!unseen_done && unseen)
165 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_unseen), n);
166 if (!flagged_done && flagged)
167 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_flagged), n);
168 if (!replied_done && replied)
169 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_replied), n);
170
171 mutt_file_fclose(&fp_new);
172
173 unlink(sequences);
174 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
175 unlink(tmpfname);
176
177 FREE(&tmpfname);
178}
179
187static void mh_seq_write_one(FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
188{
189 fprintf(fp, "%s:", tag);
190
191 int first = -1;
192 int last = -1;
193
194 for (int i = 0; i <= mhs->max; i++)
195 {
196 if ((mh_seq_check(mhs, i) & f))
197 {
198 if (first < 0)
199 first = i;
200 else
201 last = i;
202 }
203 else if (first >= 0)
204 {
205 if (last < 0)
206 fprintf(fp, " %d", first);
207 else
208 fprintf(fp, " %d-%d", first, last);
209
210 first = -1;
211 last = -1;
212 }
213 }
214
215 if (first >= 0)
216 {
217 if (last < 0)
218 fprintf(fp, " %d", first);
219 else
220 fprintf(fp, " %d-%d", first, last);
221 }
222
223 fputc('\n', fp);
224}
225
233void mh_seq_update(struct Mailbox *m)
234{
235 char sequences[PATH_MAX] = { 0 };
236 char *tmpfname = NULL;
237 char *buf = NULL;
238 char *p = NULL;
239 size_t s;
240 int seq_num = 0;
241
242 int unseen = 0;
243 int flagged = 0;
244 int replied = 0;
245
246 char seq_unseen[256] = { 0 };
247 char seq_replied[256] = { 0 };
248 char seq_flagged[256] = { 0 };
249
250 struct MhSequences mhs = { 0 };
251
252 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
253 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
254 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
255 snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
256 snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
257 snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
258
259 FILE *fp_new = NULL;
260 if (!mh_mkstemp(m, &fp_new, &tmpfname))
261 {
262 /* error message? */
263 return;
264 }
265
266 snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
267
268 /* first, copy unknown sequences */
269 FILE *fp_old = fopen(sequences, "r");
270 if (fp_old)
271 {
272 while ((buf = mutt_file_read_line(buf, &s, fp_old, NULL, MUTT_RL_NO_FLAGS)))
273 {
274 if (mutt_str_startswith(buf, seq_unseen) || mutt_str_startswith(buf, seq_flagged) ||
275 mutt_str_startswith(buf, seq_replied))
276 {
277 continue;
278 }
279
280 fprintf(fp_new, "%s\n", buf);
281 }
282 }
283 mutt_file_fclose(&fp_old);
284
285 /* now, update our unseen, flagged, and replied sequences */
286 for (int i = 0; i < m->msg_count; i++)
287 {
288 struct Email *e = m->emails[i];
289 if (!e)
290 break;
291
292 if (e->deleted)
293 continue;
294
295 p = strrchr(e->path, '/');
296 if (p)
297 p++;
298 else
299 p = e->path;
300
301 if (!mutt_str_atoi_full(p, &seq_num))
302 continue;
303
304 if (!e->read)
305 {
306 mh_seq_set(&mhs, seq_num, MH_SEQ_UNSEEN);
307 unseen++;
308 }
309 if (e->flagged)
310 {
311 mh_seq_set(&mhs, seq_num, MH_SEQ_FLAGGED);
312 flagged++;
313 }
314 if (e->replied)
315 {
316 mh_seq_set(&mhs, seq_num, MH_SEQ_REPLIED);
317 replied++;
318 }
319 }
320
321 /* write out the new sequences */
322 if (unseen)
323 mh_seq_write_one(fp_new, &mhs, MH_SEQ_UNSEEN, NONULL(c_mh_seq_unseen));
324 if (flagged)
325 mh_seq_write_one(fp_new, &mhs, MH_SEQ_FLAGGED, NONULL(c_mh_seq_flagged));
326 if (replied)
327 mh_seq_write_one(fp_new, &mhs, MH_SEQ_REPLIED, NONULL(c_mh_seq_replied));
328
329 mh_seq_free(&mhs);
330
331 /* try to commit the changes - no guarantee here */
332 mutt_file_fclose(&fp_new);
333
334 unlink(sequences);
335 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
336 {
337 /* report an error? */
338 unlink(tmpfname);
339 }
340
341 FREE(&tmpfname);
342}
343
352static int mh_seq_read_token(char *t, int *first, int *last)
353{
354 char *p = strchr(t, '-');
355 if (p)
356 {
357 *p++ = '\0';
358 if (!mutt_str_atoi_full(t, first) || !mutt_str_atoi_full(p, last))
359 return -1;
360 }
361 else
362 {
363 if (!mutt_str_atoi_full(t, first))
364 return -1;
365 *last = *first;
366 }
367 return 0;
368}
369
377int mh_seq_read(struct MhSequences *mhs, const char *path)
378{
379 char *buf = NULL;
380 size_t sz = 0;
381
382 MhSeqFlags flags;
383 int first, last, rc = 0;
384
385 char pathname[PATH_MAX] = { 0 };
386 snprintf(pathname, sizeof(pathname), "%s/.mh_sequences", path);
387
388 FILE *fp = fopen(pathname, "r");
389 if (!fp)
390 return 0; /* yes, ask callers to silently ignore the error */
391
392 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
393 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
394 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
395 while ((buf = mutt_file_read_line(buf, &sz, fp, NULL, MUTT_RL_NO_FLAGS)))
396 {
397 char *t = strtok(buf, " \t:");
398 if (!t)
399 continue;
400
401 if (mutt_str_equal(t, c_mh_seq_unseen))
402 flags = MH_SEQ_UNSEEN;
403 else if (mutt_str_equal(t, c_mh_seq_flagged))
404 flags = MH_SEQ_FLAGGED;
405 else if (mutt_str_equal(t, c_mh_seq_replied))
406 flags = MH_SEQ_REPLIED;
407 else /* unknown sequence */
408 continue;
409
410 while ((t = strtok(NULL, " \t:")))
411 {
412 if (mh_seq_read_token(t, &first, &last) < 0)
413 {
414 mh_seq_free(mhs);
415 rc = -1;
416 goto out;
417 }
418 for (; first <= last; first++)
419 mh_seq_set(mhs, first, flags);
420 }
421 }
422
423 rc = 0;
424
425out:
426 FREE(&buf);
427 mutt_file_fclose(&fp);
428 return rc;
429}
430
439{
440 char path[PATH_MAX] = { 0 };
441 struct stat st = { 0 };
442
443 if ((snprintf(path, sizeof(path), "%s/.mh_sequences", mailbox_path(m)) < sizeof(path)) &&
444 (stat(path, &st) == 0))
445 {
447 }
448 return -1;
449}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
Structs that make up an email.
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:738
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:344
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
int mutt_file_stat_timespec_compare(struct stat *st, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1638
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:39
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: mh.c:78
Convenience wrapper for the library headers.
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:497
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
#define PATH_MAX
Definition: mutt.h:41
void mh_seq_add_one(struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
Update the flags for one sequence.
Definition: sequence.c:107
static void mh_seq_alloc(struct MhSequences *mhs, int i)
Allocate more memory for sequences.
Definition: sequence.c:49
MhSeqFlags mh_seq_check(struct MhSequences *mhs, int i)
Get the flags for a given sequence.
Definition: sequence.c:78
void mh_seq_free(struct MhSequences *mhs)
Free some sequences.
Definition: sequence.c:67
static MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
Set a flag for a given sequence.
Definition: sequence.c:92
static int mh_seq_read_token(char *t, int *first, int *last)
Parse a number, or number range.
Definition: sequence.c:352
int mh_seq_changed(struct Mailbox *m)
Has the mailbox changed.
Definition: sequence.c:438
void mh_seq_update(struct Mailbox *m)
Update sequence numbers.
Definition: sequence.c:233
int mh_seq_read(struct MhSequences *mhs, const char *path)
Read a set of MH sequences.
Definition: sequence.c:377
static void mh_seq_write_one(FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
Write a flag sequence to a file.
Definition: sequence.c:187
MH Mailbox Sequences.
#define MH_SEQ_UNSEEN
Email hasn't been read.
Definition: sequence.h:33
#define MH_SEQ_REPLIED
Email has been replied to.
Definition: sequence.h:34
uint8_t MhSeqFlags
Flags, e.g. MH_SEQ_UNSEEN.
Definition: sequence.h:31
#define MH_SEQ_FLAGGED
Email is flagged.
Definition: sequence.h:35
GUI display the mailboxes in a side panel.
#define NONULL(x)
Definition: string2.h:37
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
bool deleted
Email is deleted.
Definition: email.h:76
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:105
Set of MH sequence numbers.
Definition: sequence.h:41
MhSeqFlags * flags
Flags for each email.
Definition: sequence.h:43
int max
Number of flags stored.
Definition: sequence.h:42
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39