NeoMutt  2022-04-29-215-gc12b98
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
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 fprintf(fp_new, "%s\n", buf);
157 }
158 }
159 mutt_file_fclose(&fp_old);
160 FREE(&buf);
161
162 if (!unseen_done && unseen)
163 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_unseen), n);
164 if (!flagged_done && flagged)
165 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_flagged), n);
166 if (!replied_done && replied)
167 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_replied), n);
168
169 mutt_file_fclose(&fp_new);
170
171 unlink(sequences);
172 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
173 unlink(tmpfname);
174
175 FREE(&tmpfname);
176}
177
185static void mh_seq_write_one(FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
186{
187 fprintf(fp, "%s:", tag);
188
189 int first = -1;
190 int last = -1;
191
192 for (int i = 0; i <= mhs->max; i++)
193 {
194 if ((mh_seq_check(mhs, i) & f))
195 {
196 if (first < 0)
197 first = i;
198 else
199 last = i;
200 }
201 else if (first >= 0)
202 {
203 if (last < 0)
204 fprintf(fp, " %d", first);
205 else
206 fprintf(fp, " %d-%d", first, last);
207
208 first = -1;
209 last = -1;
210 }
211 }
212
213 if (first >= 0)
214 {
215 if (last < 0)
216 fprintf(fp, " %d", first);
217 else
218 fprintf(fp, " %d-%d", first, last);
219 }
220
221 fputc('\n', fp);
222}
223
231void mh_seq_update(struct Mailbox *m)
232{
233 char sequences[PATH_MAX] = { 0 };
234 char *tmpfname = NULL;
235 char *buf = NULL;
236 char *p = NULL;
237 size_t s;
238 int seq_num = 0;
239
240 int unseen = 0;
241 int flagged = 0;
242 int replied = 0;
243
244 char seq_unseen[256] = { 0 };
245 char seq_replied[256] = { 0 };
246 char seq_flagged[256] = { 0 };
247
248 struct MhSequences mhs = { 0 };
249
250 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
251 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
252 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
253 snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
254 snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
255 snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
256
257 FILE *fp_new = NULL;
258 if (!mh_mkstemp(m, &fp_new, &tmpfname))
259 {
260 /* error message? */
261 return;
262 }
263
264 snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
265
266 /* first, copy unknown sequences */
267 FILE *fp_old = fopen(sequences, "r");
268 if (fp_old)
269 {
270 while ((buf = mutt_file_read_line(buf, &s, fp_old, NULL, MUTT_RL_NO_FLAGS)))
271 {
272 if (mutt_str_startswith(buf, seq_unseen) || mutt_str_startswith(buf, seq_flagged) ||
273 mutt_str_startswith(buf, seq_replied))
274 {
275 continue;
276 }
277
278 fprintf(fp_new, "%s\n", buf);
279 }
280 }
281 mutt_file_fclose(&fp_old);
282
283 /* now, update our unseen, flagged, and replied sequences */
284 for (int i = 0; i < m->msg_count; i++)
285 {
286 struct Email *e = m->emails[i];
287 if (!e)
288 break;
289
290 if (e->deleted)
291 continue;
292
293 p = strrchr(e->path, '/');
294 if (p)
295 p++;
296 else
297 p = e->path;
298
299 if (!mutt_str_atoi_full(p, &seq_num))
300 continue;
301
302 if (!e->read)
303 {
304 mh_seq_set(&mhs, seq_num, MH_SEQ_UNSEEN);
305 unseen++;
306 }
307 if (e->flagged)
308 {
309 mh_seq_set(&mhs, seq_num, MH_SEQ_FLAGGED);
310 flagged++;
311 }
312 if (e->replied)
313 {
314 mh_seq_set(&mhs, seq_num, MH_SEQ_REPLIED);
315 replied++;
316 }
317 }
318
319 /* write out the new sequences */
320 if (unseen)
321 mh_seq_write_one(fp_new, &mhs, MH_SEQ_UNSEEN, NONULL(c_mh_seq_unseen));
322 if (flagged)
323 mh_seq_write_one(fp_new, &mhs, MH_SEQ_FLAGGED, NONULL(c_mh_seq_flagged));
324 if (replied)
325 mh_seq_write_one(fp_new, &mhs, MH_SEQ_REPLIED, NONULL(c_mh_seq_replied));
326
327 mh_seq_free(&mhs);
328
329 /* try to commit the changes - no guarantee here */
330 mutt_file_fclose(&fp_new);
331
332 unlink(sequences);
333 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
334 {
335 /* report an error? */
336 unlink(tmpfname);
337 }
338
339 FREE(&tmpfname);
340}
341
350static int mh_seq_read_token(char *t, int *first, int *last)
351{
352 char *p = strchr(t, '-');
353 if (p)
354 {
355 *p++ = '\0';
356 if (!mutt_str_atoi_full(t, first) || !mutt_str_atoi_full(p, last))
357 return -1;
358 }
359 else
360 {
361 if (!mutt_str_atoi_full(t, first))
362 return -1;
363 *last = *first;
364 }
365 return 0;
366}
367
375int mh_seq_read(struct MhSequences *mhs, const char *path)
376{
377 char *buf = NULL;
378 size_t sz = 0;
379
380 MhSeqFlags flags;
381 int first, last, rc = 0;
382
383 char pathname[PATH_MAX] = { 0 };
384 snprintf(pathname, sizeof(pathname), "%s/.mh_sequences", path);
385
386 FILE *fp = fopen(pathname, "r");
387 if (!fp)
388 return 0; /* yes, ask callers to silently ignore the error */
389
390 while ((buf = mutt_file_read_line(buf, &sz, fp, NULL, MUTT_RL_NO_FLAGS)))
391 {
392 char *t = strtok(buf, " \t:");
393 if (!t)
394 continue;
395
396 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
397 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
398 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
399 if (mutt_str_equal(t, c_mh_seq_unseen))
400 flags = MH_SEQ_UNSEEN;
401 else if (mutt_str_equal(t, c_mh_seq_flagged))
402 flags = MH_SEQ_FLAGGED;
403 else if (mutt_str_equal(t, c_mh_seq_replied))
404 flags = MH_SEQ_REPLIED;
405 else /* unknown sequence */
406 continue;
407
408 while ((t = strtok(NULL, " \t:")))
409 {
410 if (mh_seq_read_token(t, &first, &last) < 0)
411 {
412 mh_seq_free(mhs);
413 rc = -1;
414 goto out;
415 }
416 for (; first <= last; first++)
417 mh_seq_set(mhs, first, flags);
418 }
419 }
420
421 rc = 0;
422
423out:
424 FREE(&buf);
425 mutt_file_fclose(&fp);
426 return rc;
427}
428
437{
438 char path[PATH_MAX] = { 0 };
439 struct stat st = { 0 };
440
441 if ((snprintf(path, sizeof(path), "%s/.mh_sequences", mailbox_path(m)) < sizeof(path)) &&
442 (stat(path, &st) == 0))
443 {
445 }
446 return -1;
447}
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:720
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:152
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:1651
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:63
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
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:807
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:496
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
#define PATH_MAX
Definition: mutt.h:40
MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
Set a flag for a given sequence.
Definition: sequence.c:92
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 int mh_seq_read_token(char *t, int *first, int *last)
Parse a number, or number range.
Definition: sequence.c:350
int mh_seq_changed(struct Mailbox *m)
Has the mailbox changed.
Definition: sequence.c:436
void mh_seq_update(struct Mailbox *m)
Update sequence numbers.
Definition: sequence.c:231
int mh_seq_read(struct MhSequences *mhs, const char *path)
Read a set of MH sequences.
Definition: sequence.c:375
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:185
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