NeoMutt  2024-12-12-14-g7b49f7
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
sequence.c
Go to the documentation of this file.
1
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 (!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 mh_seq_alloc(mhs, i);
96 mhs->flags[i] |= f;
97 return mhs->flags[i];
98}
99
108void mh_seq_add_one(struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
109{
110 bool unseen_done = false;
111 bool flagged_done = false;
112 bool replied_done = false;
113
114 char *tmpfname = NULL;
115 char sequences[PATH_MAX] = { 0 };
116
117 char seq_unseen[256] = { 0 };
118 char seq_replied[256] = { 0 };
119 char seq_flagged[256] = { 0 };
120
121 char *buf = NULL;
122 size_t sz;
123
124 FILE *fp_new = NULL;
125 if (!mh_mkstemp(m, &fp_new, &tmpfname))
126 return;
127
128 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
129 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
130 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
131 snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
132 snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
133 snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
134
135 snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
136 FILE *fp_old = mutt_file_fopen(sequences, "r");
137 if (fp_old)
138 {
139 while ((buf = mutt_file_read_line(buf, &sz, fp_old, NULL, MUTT_RL_NO_FLAGS)))
140 {
141 if (unseen && mutt_strn_equal(buf, seq_unseen, mutt_str_len(seq_unseen)))
142 {
143 fprintf(fp_new, "%s %d\n", buf, n);
144 unseen_done = true;
145 }
146 else if (flagged && mutt_strn_equal(buf, seq_flagged, mutt_str_len(seq_flagged)))
147 {
148 fprintf(fp_new, "%s %d\n", buf, n);
149 flagged_done = true;
150 }
151 else if (replied && mutt_strn_equal(buf, seq_replied, mutt_str_len(seq_replied)))
152 {
153 fprintf(fp_new, "%s %d\n", buf, n);
154 replied_done = true;
155 }
156 else
157 {
158 fprintf(fp_new, "%s\n", buf);
159 }
160 }
161 }
162 mutt_file_fclose(&fp_old);
163 FREE(&buf);
164
165 if (!unseen_done && unseen)
166 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_unseen), n);
167 if (!flagged_done && flagged)
168 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_flagged), n);
169 if (!replied_done && replied)
170 fprintf(fp_new, "%s: %d\n", NONULL(c_mh_seq_replied), n);
171
172 mutt_file_fclose(&fp_new);
173
174 unlink(sequences);
175 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
176 unlink(tmpfname);
177
178 FREE(&tmpfname);
179}
180
188static void mh_seq_write_one(FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
189{
190 fprintf(fp, "%s:", tag);
191
192 int first = -1;
193 int last = -1;
194
195 for (int i = 0; i <= mhs->max; i++)
196 {
197 if ((mh_seq_check(mhs, i) & f))
198 {
199 if (first < 0)
200 first = i;
201 else
202 last = i;
203 }
204 else if (first >= 0)
205 {
206 if (last < 0)
207 fprintf(fp, " %d", first);
208 else
209 fprintf(fp, " %d-%d", first, last);
210
211 first = -1;
212 last = -1;
213 }
214 }
215
216 if (first >= 0)
217 {
218 if (last < 0)
219 fprintf(fp, " %d", first);
220 else
221 fprintf(fp, " %d-%d", first, last);
222 }
223
224 fputc('\n', fp);
225}
226
234void mh_seq_update(struct Mailbox *m)
235{
236 char sequences[PATH_MAX] = { 0 };
237 char *tmpfname = NULL;
238 char *buf = NULL;
239 char *p = NULL;
240 size_t s;
241 int seq_num = 0;
242
243 int unseen = 0;
244 int flagged = 0;
245 int replied = 0;
246
247 char seq_unseen[256] = { 0 };
248 char seq_replied[256] = { 0 };
249 char seq_flagged[256] = { 0 };
250
251 struct MhSequences mhs = { 0 };
252
253 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
254 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
255 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
256 snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
257 snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
258 snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
259
260 FILE *fp_new = NULL;
261 if (!mh_mkstemp(m, &fp_new, &tmpfname))
262 {
263 /* error message? */
264 return;
265 }
266
267 snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
268
269 /* first, copy unknown sequences */
270 FILE *fp_old = mutt_file_fopen(sequences, "r");
271 if (fp_old)
272 {
273 while ((buf = mutt_file_read_line(buf, &s, fp_old, NULL, MUTT_RL_NO_FLAGS)))
274 {
275 if (mutt_str_startswith(buf, seq_unseen) || mutt_str_startswith(buf, seq_flagged) ||
276 mutt_str_startswith(buf, seq_replied))
277 {
278 continue;
279 }
280
281 fprintf(fp_new, "%s\n", buf);
282 }
283 }
284 mutt_file_fclose(&fp_old);
285
286 /* now, update our unseen, flagged, and replied sequences */
287 for (int i = 0; i < m->msg_count; i++)
288 {
289 struct Email *e = m->emails[i];
290 if (!e)
291 break;
292
293 if (e->deleted)
294 continue;
295
296 p = strrchr(e->path, '/');
297 if (p)
298 p++;
299 else
300 p = e->path;
301
302 if (!mutt_str_atoi_full(p, &seq_num))
303 continue;
304
305 if (!e->read)
306 {
307 mh_seq_set(&mhs, seq_num, MH_SEQ_UNSEEN);
308 unseen++;
309 }
310 if (e->flagged)
311 {
312 mh_seq_set(&mhs, seq_num, MH_SEQ_FLAGGED);
313 flagged++;
314 }
315 if (e->replied)
316 {
317 mh_seq_set(&mhs, seq_num, MH_SEQ_REPLIED);
318 replied++;
319 }
320 }
321
322 /* write out the new sequences */
323 if (unseen)
324 mh_seq_write_one(fp_new, &mhs, MH_SEQ_UNSEEN, NONULL(c_mh_seq_unseen));
325 if (flagged)
326 mh_seq_write_one(fp_new, &mhs, MH_SEQ_FLAGGED, NONULL(c_mh_seq_flagged));
327 if (replied)
328 mh_seq_write_one(fp_new, &mhs, MH_SEQ_REPLIED, NONULL(c_mh_seq_replied));
329
330 mh_seq_free(&mhs);
331
332 /* try to commit the changes - no guarantee here */
333 mutt_file_fclose(&fp_new);
334
335 unlink(sequences);
336 if (mutt_file_safe_rename(tmpfname, sequences) != 0)
337 {
338 /* report an error? */
339 unlink(tmpfname);
340 }
341
342 FREE(&tmpfname);
343}
344
353static int mh_seq_read_token(char *t, int *first, int *last)
354{
355 char *p = strchr(t, '-');
356 if (p)
357 {
358 *p++ = '\0';
359 if (!mutt_str_atoi_full(t, first) || !mutt_str_atoi_full(p, last))
360 return -1;
361 }
362 else
363 {
364 if (!mutt_str_atoi_full(t, first))
365 return -1;
366 *last = *first;
367 }
368 return 0;
369}
370
378int mh_seq_read(struct MhSequences *mhs, const char *path)
379{
380 char *buf = NULL;
381 size_t sz = 0;
382
383 MhSeqFlags flags;
384 int first, last, rc = 0;
385
386 char pathname[PATH_MAX] = { 0 };
387 snprintf(pathname, sizeof(pathname), "%s/.mh_sequences", path);
388
389 FILE *fp = mutt_file_fopen(pathname, "r");
390 if (!fp)
391 return 0; /* yes, ask callers to silently ignore the error */
392
393 const char *const c_mh_seq_unseen = cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
394 const char *const c_mh_seq_flagged = cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
395 const char *const c_mh_seq_replied = cs_subset_string(NeoMutt->sub, "mh_seq_replied");
396 while ((buf = mutt_file_read_line(buf, &sz, fp, NULL, MUTT_RL_NO_FLAGS)))
397 {
398 char *t = strtok(buf, " \t:");
399 if (!t)
400 continue;
401
402 if (mutt_str_equal(t, c_mh_seq_unseen))
403 flags = MH_SEQ_UNSEEN;
404 else if (mutt_str_equal(t, c_mh_seq_flagged))
405 flags = MH_SEQ_FLAGGED;
406 else if (mutt_str_equal(t, c_mh_seq_replied))
407 flags = MH_SEQ_REPLIED;
408 else /* unknown sequence */
409 continue;
410
411 while ((t = strtok(NULL, " \t:")))
412 {
413 if (mh_seq_read_token(t, &first, &last) < 0)
414 {
415 mh_seq_free(mhs);
416 rc = -1;
417 goto out;
418 }
419 for (; first <= last; first++)
420 mh_seq_set(mhs, first, flags);
421 }
422 }
423
424 rc = 0;
425
426out:
427 FREE(&buf);
428 mutt_file_fclose(&fp);
429 return rc;
430}
431
440{
441 char path[PATH_MAX] = { 0 };
442 struct stat st = { 0 };
443
444 if ((snprintf(path, sizeof(path), "%s/.mh_sequences", mailbox_path(m)) < sizeof(path)) &&
445 (stat(path, &st) == 0))
446 {
448 }
449 return -1;
450}
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:223
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:808
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:371
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:1619
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
#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)
Definition: memory.h:55
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
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:660
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:425
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
#define PATH_MAX
Definition: mutt.h:42
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:108
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:353
int mh_seq_changed(struct Mailbox *m)
Has the mailbox changed.
Definition: sequence.c:439
void mh_seq_update(struct Mailbox *m)
Update sequence numbers.
Definition: sequence.c:234
int mh_seq_read(struct MhSequences *mhs, const char *path)
Read a set of MH sequences.
Definition: sequence.c:378
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:188
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:37
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: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:104
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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46