NeoMutt  2024-10-02-37-gfa9146
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "sequence.h"
#include "shared.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.
 
void mh_seq_free (struct MhSequences *mhs)
 Free some sequences.
 
MhSeqFlags mh_seq_check (struct MhSequences *mhs, int i)
 Get the flags for a given sequence.
 
static MhSeqFlags mh_seq_set (struct MhSequences *mhs, int i, MhSeqFlags f)
 Set a flag for a given sequence.
 
void mh_seq_add_one (struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
 Update the flags for one sequence.
 
static void mh_seq_write_one (FILE *fp, struct MhSequences *mhs, MhSeqFlags f, const char *tag)
 Write a flag sequence to a file.
 
void mh_seq_update (struct Mailbox *m)
 Update sequence numbers.
 
static int mh_seq_read_token (char *t, int *first, int *last)
 Parse a number, or number range.
 
int mh_seq_read (struct MhSequences *mhs, const char *path)
 Read a set of MH sequences.
 
int mh_seq_changed (struct Mailbox *m)
 Has the mailbox changed.
 

Detailed Description

MH Mailbox Sequences.

Authors
  • Pietro Cerutti
  • 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 50 of file sequence.c.

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, sizeof(mhs->flags[0]) * (newmax + 1));
58 while (j <= newmax)
59 mhs->flags[j++] = 0;
60
61 mhs->max = newmax;
62}
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:115
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 68 of file sequence.c.

69{
70 FREE(&mhs->flags);
71}
#define FREE(x)
Definition: memory.h:45
+ 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 79 of file sequence.c.

80{
81 if (!mhs->flags || (i > mhs->max))
82 return 0;
83 return mhs->flags[i];
84}
+ 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 93 of file sequence.c.

94{
95 mh_seq_alloc(mhs, i);
96 mhs->flags[i] |= f;
97 return mhs->flags[i];
98}
static void mh_seq_alloc(struct MhSequences *mhs, int i)
Allocate more memory for sequences.
Definition: sequence.c:50
+ 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 108 of file sequence.c.

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}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:223
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
#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
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: shared.c:73
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_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
#define PATH_MAX
Definition: mutt.h:42
#define NONULL(x)
Definition: string2.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
+ 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 188 of file sequence.c.

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}
MhSeqFlags mh_seq_check(struct MhSequences *mhs, int i)
Get the flags for a given sequence.
Definition: sequence.c:79
+ 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 234 of file sequence.c.

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}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
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 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
#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: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
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 353 of file sequence.c.

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}
+ 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 378 of file sequence.c.

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}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
static int mh_seq_read_token(char *t, int *first, int *last)
Parse a number, or number range.
Definition: sequence.c:353
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 439 of file sequence.c.

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