NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
sequence.c File Reference

MH Mailbox Sequences. More...

#include "config.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "sequence.h"
+ Include dependency graph for sequence.c:

Go to the source code of this file.

Functions

static void mh_seq_alloc (struct MhSequences *mhs, int i)
 Allocate more memory for sequences. More...
 
void mh_seq_free (struct MhSequences *mhs)
 Free some sequences. More...
 
MhSeqFlags mh_seq_check (struct MhSequences *mhs, int i)
 Get the flags for a given sequence. More...
 
static MhSeqFlags mh_seq_set (struct MhSequences *mhs, int i, MhSeqFlags f)
 Set a flag for a given sequence. More...
 
void mh_seq_add_one (struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
 Update the flags for one sequence. More...
 
static void mh_seq_write_one (FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
 Write a flag sequence to a file. More...
 
void mh_seq_update (struct Mailbox *m)
 Update sequence numbers. More...
 
static int mh_seq_read_token (char *t, int *first, int *last)
 Parse a number, or number range. More...
 
int mh_seq_read (struct MhSequences *mhs, const char *path)
 Read a set of MH sequences. More...
 
int mh_seq_changed (struct Mailbox *m)
 Has the mailbox changed. More...
 

Detailed Description

MH Mailbox Sequences.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file sequence.c.

Function Documentation

◆ mh_seq_alloc()

static void mh_seq_alloc ( struct MhSequences mhs,
int  i 
)
static

Allocate more memory for sequences.

Parameters
mhsExisting sequences
iNumber required
Note
Memory is allocated in blocks of 128.

Definition at line 49 of file sequence.c.

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}
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
MhSeqFlags * flags
Flags for each email.
Definition: sequence.h:43
int max
Number of flags stored.
Definition: sequence.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_free()

void mh_seq_free ( struct MhSequences mhs)

Free some sequences.

Parameters
mhsSequences to free

Definition at line 67 of file sequence.c.

68{
69 FREE(&mhs->flags);
70}
#define FREE(x)
Definition: memory.h:43
+ Here is the caller graph for this function:

◆ mh_seq_check()

MhSeqFlags mh_seq_check ( struct MhSequences mhs,
int  i 
)

Get the flags for a given sequence.

Parameters
mhsSequences
iIndex number required
Return values
numFlags, see MhSeqFlags

Definition at line 78 of file sequence.c.

79{
80 if (!mhs->flags || (i > mhs->max))
81 return 0;
82 return mhs->flags[i];
83}
+ Here is the caller graph for this function:

◆ mh_seq_set()

static MhSeqFlags mh_seq_set ( struct MhSequences mhs,
int  i,
MhSeqFlags  f 
)
static

Set a flag for a given sequence.

Parameters
mhsSequences
iIndex number
fFlags, see MhSeqFlags
Return values
numResulting flags, see MhSeqFlags

Definition at line 92 of file sequence.c.

93{
94 mh_seq_alloc(mhs, i);
95 mhs->flags[i] |= f;
96 return mhs->flags[i];
97}
static void mh_seq_alloc(struct MhSequences *mhs, int i)
Allocate more memory for sequences.
Definition: sequence.c:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_add_one()

void mh_seq_add_one ( struct Mailbox m,
int  n,
bool  unseen,
bool  flagged,
bool  replied 
)

Update the flags for one sequence.

Parameters
mMailbox
nSequence number to update
unseenUpdate the unseen sequence
flaggedUpdate the flagged sequence
repliedUpdate the replied sequence

Definition at line 107 of file sequence.c.

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}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
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:736
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:343
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:39
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: mh.c:78
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_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
#define PATH_MAX
Definition: mutt.h:41
#define NONULL(x)
Definition: string2.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_write_one()

static void mh_seq_write_one ( FILE *  fp,
struct MhSequences mhs,
MhSeqFlags  f,
const char *  tag 
)
static

Write a flag sequence to a file.

Parameters
fpFile to write to
mhsSequence list
fFlag, see MhSeqFlags
tagstring tag, e.g. "unseen"

Definition at line 185 of file sequence.c.

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}
MhSeqFlags mh_seq_check(struct MhSequences *mhs, int i)
Get the flags for a given sequence.
Definition: sequence.c:78
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_update()

void mh_seq_update ( struct Mailbox m)

Update sequence numbers.

Parameters
mMailbox

XXX we don't currently remove deleted messages from sequences we don't know. Should we?

Definition at line 231 of file sequence.c.

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}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
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 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
#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
#define MH_SEQ_FLAGGED
Email is flagged.
Definition: sequence.h:35
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
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
Set of MH sequence numbers.
Definition: sequence.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_read_token()

static int mh_seq_read_token ( char *  t,
int *  first,
int *  last 
)
static

Parse a number, or number range.

Parameters
tString to parse
firstFirst number
lastLast number (if a range, first number if not)
Return values
0Success
-1Error

Definition at line 350 of file sequence.c.

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}
+ Here is the caller graph for this function:

◆ mh_seq_read()

int mh_seq_read ( struct MhSequences mhs,
const char *  path 
)

Read a set of MH sequences.

Parameters
mhsExisting sequences
pathFile to read from
Return values
0Success
-1Error

Definition at line 375 of file sequence.c.

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}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
static int mh_seq_read_token(char *t, int *first, int *last)
Parse a number, or number range.
Definition: sequence.c:350
uint8_t MhSeqFlags
Flags, e.g. MH_SEQ_UNSEEN.
Definition: sequence.h:31
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_seq_changed()

int mh_seq_changed ( struct Mailbox m)

Has the mailbox changed.

Parameters
mMailbox
Return values
1mh_sequences last modification time is more recent than the last visit to this mailbox
0modification time is older
-1Error

Definition at line 436 of file sequence.c.

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}
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:1667
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:105
+ Here is the call graph for this function:
+ Here is the caller graph for this function: