NeoMutt  2022-04-29-145-g9b6a0e
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 }
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()

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 }
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 = 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
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:344
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
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:720
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
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:473
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
#define PATH_MAX
Definition: mutt.h:40
#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];
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];
245  char seq_replied[256];
246  char seq_flagged[256];
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
MhSeqFlags mh_seq_set(struct MhSequences *mhs, int i, MhSeqFlags f)
Set a flag for a given sequence.
Definition: sequence.c:92
void mh_seq_free(struct MhSequences *mhs)
Free some sequences.
Definition: sequence.c:67
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:

◆ 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];
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 
423 out:
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:784
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:29
+ 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];
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:1651
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:63
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:105
+ Here is the call graph for this function: