NeoMutt  2021-02-05-666-ge300cd
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...
 
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 }
int max
Number of flags stored.
Definition: sequence.h:42
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
+ 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 }
MhSeqFlags * flags
Flags for each email.
Definition: sequence.h:43
#define FREE(x)
Definition: memory.h:40
+ 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 }
int max
Number of flags stored.
Definition: sequence.h:42
MhSeqFlags * flags
Flags for each email.
Definition: sequence.h:43
+ Here is the caller graph for this function:

◆ mh_seq_set()

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

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 }
MhSeqFlags * flags
Flags for each email.
Definition: sequence.h:43
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];
115 
116  char seq_unseen[256];
117  char seq_replied[256];
118  char seq_flagged[256];
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 =
128  cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
129  const char *const c_mh_seq_replied =
130  cs_subset_string(NeoMutt->sub, "mh_seq_replied");
131  const char *const c_mh_seq_flagged =
132  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 = 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  fprintf(fp_new, "%s\n", buf);
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 }
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
#define NONULL(x)
Definition: string2.h:37
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:667
Container for Accounts, Notifications.
Definition: neomutt.h:36
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define PATH_MAX
Definition: mutt.h:40
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: mh.c:77
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
bool flagged
Marked important?
Definition: email.h:43
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:593
bool replied
Email has been replied to.
Definition: email.h:54
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
#define FREE(x)
Definition: memory.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:354
+ 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:78
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_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];
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];
248  char seq_replied[256];
249  char seq_flagged[256];
250 
251  struct MhSequences mhs = { 0 };
252 
253  const char *const c_mh_seq_unseen =
254  cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
255  const char *const c_mh_seq_replied =
256  cs_subset_string(NeoMutt->sub, "mh_seq_replied");
257  const char *const c_mh_seq_flagged =
258  cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
259  snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(c_mh_seq_unseen));
260  snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(c_mh_seq_replied));
261  snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(c_mh_seq_flagged));
262 
263  FILE *fp_new = NULL;
264  if (!mh_mkstemp(m, &fp_new, &tmpfname))
265  {
266  /* error message? */
267  return;
268  }
269 
270  snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", mailbox_path(m));
271 
272  /* first, copy unknown sequences */
273  FILE *fp_old = fopen(sequences, "r");
274  if (fp_old)
275  {
276  while ((buf = mutt_file_read_line(buf, &s, fp_old, NULL, MUTT_RL_NO_FLAGS)))
277  {
278  if (mutt_str_startswith(buf, seq_unseen) || mutt_str_startswith(buf, seq_flagged) ||
279  mutt_str_startswith(buf, seq_replied))
280  {
281  continue;
282  }
283 
284  fprintf(fp_new, "%s\n", buf);
285  }
286  }
287  mutt_file_fclose(&fp_old);
288 
289  /* now, update our unseen, flagged, and replied sequences */
290  for (int i = 0; i < m->msg_count; i++)
291  {
292  struct Email *e = m->emails[i];
293  if (!e)
294  break;
295 
296  if (e->deleted)
297  continue;
298 
299  p = strrchr(e->path, '/');
300  if (p)
301  p++;
302  else
303  p = e->path;
304 
305  if (mutt_str_atoi(p, &seq_num) < 0)
306  continue;
307 
308  if (!e->read)
309  {
310  mh_seq_set(&mhs, seq_num, MH_SEQ_UNSEEN);
311  unseen++;
312  }
313  if (e->flagged)
314  {
315  mh_seq_set(&mhs, seq_num, MH_SEQ_FLAGGED);
316  flagged++;
317  }
318  if (e->replied)
319  {
320  mh_seq_set(&mhs, seq_num, MH_SEQ_REPLIED);
321  replied++;
322  }
323  }
324 
325  /* write out the new sequences */
326  if (unseen)
327  mh_seq_write_one(fp_new, &mhs, MH_SEQ_UNSEEN, NONULL(c_mh_seq_unseen));
328  if (flagged)
329  mh_seq_write_one(fp_new, &mhs, MH_SEQ_FLAGGED, NONULL(c_mh_seq_flagged));
330  if (replied)
331  mh_seq_write_one(fp_new, &mhs, MH_SEQ_REPLIED, NONULL(c_mh_seq_replied));
332 
333  mh_seq_free(&mhs);
334 
335  /* try to commit the changes - no guarantee here */
336  mutt_file_fclose(&fp_new);
337 
338  unlink(sequences);
339  if (mutt_file_safe_rename(tmpfname, sequences) != 0)
340  {
341  /* report an error? */
342  unlink(tmpfname);
343  }
344 
345  FREE(&tmpfname);
346 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
#define NONULL(x)
Definition: string2.h:37
int msg_count
Total number of messages.
Definition: mailbox.h:91
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
The envelope/body of an email.
Definition: email.h:37
void mh_seq_free(struct MhSequences *mhs)
Free some sequences.
Definition: sequence.c:67
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:667
Set of MH sequence numbers.
Definition: sequence.h:40
Container for Accounts, Notifications.
Definition: neomutt.h:36
bool read
Email is read.
Definition: email.h:51
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define MH_SEQ_REPLIED
Email has been replied to.
Definition: sequence.h:34
MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
Set a flag for a given sequence.
Definition: sequence.c:92
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
#define PATH_MAX
Definition: mutt.h:40
#define MH_SEQ_FLAGGED
Email is flagged.
Definition: sequence.h:35
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
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
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: mh.c:77
bool flagged
Marked important?
Definition: email.h:43
bool deleted
Email is deleted.
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:54
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
#define FREE(x)
Definition: memory.h:40
#define MH_SEQ_UNSEEN
Email hasn&#39;t been read.
Definition: sequence.h:33
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:354
+ 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 356 of file sequence.c.

357 {
358  char *p = strchr(t, '-');
359  if (p)
360  {
361  *p++ = '\0';
362  if ((mutt_str_atoi(t, first) < 0) || (mutt_str_atoi(p, last) < 0))
363  return -1;
364  }
365  else
366  {
367  if (mutt_str_atoi(t, first) < 0)
368  return -1;
369  *last = *first;
370  }
371  return 0;
372 }
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
+ Here is the call graph for this function:
+ 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 381 of file sequence.c.

382 {
383  char *buf = NULL;
384  size_t sz = 0;
385 
386  MhSeqFlags flags;
387  int first, last, rc = 0;
388 
389  char pathname[PATH_MAX];
390  snprintf(pathname, sizeof(pathname), "%s/.mh_sequences", path);
391 
392  FILE *fp = fopen(pathname, "r");
393  if (!fp)
394  return 0; /* yes, ask callers to silently ignore the error */
395 
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  const char *const c_mh_seq_unseen =
403  cs_subset_string(NeoMutt->sub, "mh_seq_unseen");
404  const char *const c_mh_seq_flagged =
405  cs_subset_string(NeoMutt->sub, "mh_seq_flagged");
406  const char *const c_mh_seq_replied =
407  cs_subset_string(NeoMutt->sub, "mh_seq_replied");
408  if (mutt_str_equal(t, c_mh_seq_unseen))
409  flags = MH_SEQ_UNSEEN;
410  else if (mutt_str_equal(t, c_mh_seq_flagged))
411  flags = MH_SEQ_FLAGGED;
412  else if (mutt_str_equal(t, c_mh_seq_replied))
413  flags = MH_SEQ_REPLIED;
414  else /* unknown sequence */
415  continue;
416 
417  while ((t = strtok(NULL, " \t:")))
418  {
419  if (mh_seq_read_token(t, &first, &last) < 0)
420  {
421  mh_seq_free(mhs);
422  rc = -1;
423  goto out;
424  }
425  for (; first <= last; first++)
426  mh_seq_set(mhs, first, flags);
427  }
428  }
429 
430  rc = 0;
431 
432 out:
433  FREE(&buf);
434  mutt_file_fclose(&fp);
435  return rc;
436 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
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:356
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:667
uint8_t MhSeqFlags
Flags, e.g. MH_SEQ_UNSEEN.
Definition: sequence.h:29
Container for Accounts, Notifications.
Definition: neomutt.h:36
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define MH_SEQ_REPLIED
Email has been replied to.
Definition: sequence.h:34
MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
Set a flag for a given sequence.
Definition: sequence.c:92
#define PATH_MAX
Definition: mutt.h:40
#define MH_SEQ_FLAGGED
Email is flagged.
Definition: sequence.h:35
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
#define FREE(x)
Definition: memory.h:40
#define MH_SEQ_UNSEEN
Email hasn&#39;t been read.
Definition: sequence.h:33
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_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 445 of file sequence.c.

446 {
447  char path[PATH_MAX];
448  struct stat sb;
449 
450  if ((snprintf(path, sizeof(path), "%s/.mh_sequences", mailbox_path(m)) < sizeof(path)) &&
451  (stat(path, &sb) == 0))
452  {
454  }
455  return -1;
456 }
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:108
File/dir&#39;s mtime - last modified time.
Definition: file.h:63
#define PATH_MAX
Definition: mutt.h:40
int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1580
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
+ Here is the call graph for this function:
+ Here is the caller graph for this function: