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