NeoMutt  2024-12-12-14-g7b49f7
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
rfc3676.c File Reference

RFC3676 Format Flowed routines. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "rfc3676.h"
+ Include dependency graph for rfc3676.c:

Go to the source code of this file.

Data Structures

struct  FlowedState
 State of a Format-Flowed line of text. More...
 

Macros

#define FLOWED_MAX   72
 

Functions

static int get_quote_level (const char *line)
 Get the quote level of a line.
 
static int space_quotes (struct State *state)
 Should we add spaces between quote levels.
 
static bool add_quote_suffix (struct State *state, int ql)
 Should we add a trailing space to quotes.
 
static size_t print_indent (int ql, struct State *state, int add_suffix)
 Print indented text.
 
static void flush_par (struct State *state, struct FlowedState *fst)
 Write out the paragraph.
 
static int quote_width (struct State *state, int ql)
 Calculate the paragraph width based upon the quote level.
 
static void print_flowed_line (char *line, struct State *state, int ql, struct FlowedState *fst, bool term)
 Print a format-flowed line.
 
static void print_fixed_line (const char *line, struct State *state, int ql, struct FlowedState *fst)
 Print a fixed format line.
 
int rfc3676_handler (struct Body *b_email, struct State *state)
 Handler for format=flowed - Implements handler_t -.
 
bool mutt_rfc3676_is_format_flowed (struct Body *b)
 Is the Email "format-flowed"?
 
static void rfc3676_space_stuff (const char *filename, bool unstuff)
 Perform required RFC3676 space stuffing.
 
void mutt_rfc3676_space_stuff (struct Email *e)
 Perform RFC3676 space stuffing on an Email.
 
void mutt_rfc3676_space_unstuff (struct Email *e)
 Remove RFC3676 space stuffing.
 
void mutt_rfc3676_space_unstuff_attachment (struct Body *b, const char *filename)
 Unstuff attachments.
 
void mutt_rfc3676_space_stuff_attachment (struct Body *b, const char *filename)
 Stuff attachments.
 

Detailed Description

RFC3676 Format Flowed routines.

Authors
  • Andreas Krennmair
  • Peter J. Holzer
  • Rocco Rutte
  • Michael R. Elkins
  • Richard Russon
  • Federico Kircheis
  • Pietro Cerutti

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 rfc3676.c.

Macro Definition Documentation

◆ FLOWED_MAX

#define FLOWED_MAX   72

Definition at line 46 of file rfc3676.c.

Function Documentation

◆ get_quote_level()

static int get_quote_level ( const char *  line)
static

Get the quote level of a line.

Parameters
lineText to examine
Return values
numQuote level

Definition at line 63 of file rfc3676.c.

64{
65 int quoted = 0;
66 const char *p = line;
67
68 while (p && (*p == '>'))
69 {
70 quoted++;
71 p++;
72 }
73
74 return quoted;
75}
+ Here is the caller graph for this function:

◆ space_quotes()

static int space_quotes ( struct State state)
static

Should we add spaces between quote levels.

Parameters
stateState to use
Return values
trueSpaces should be added

Determines whether to add spacing between/after each quote level: >>>foo becomes > > > foo

Definition at line 87 of file rfc3676.c.

88{
89 /* Allow quote spacing in the pager even for `$text_flowed`,
90 * but obviously not when replying. */
91 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
92 if (c_text_flowed && (state->flags & STATE_REPLYING))
93 return 0;
94
95 const bool c_reflow_space_quotes = cs_subset_bool(NeoMutt->sub, "reflow_space_quotes");
96 return c_reflow_space_quotes;
97}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
#define STATE_REPLYING
Are we replying?
Definition: state.h:39
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition: state.h:52
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_quote_suffix()

static bool add_quote_suffix ( struct State state,
int  ql 
)
static

Should we add a trailing space to quotes.

Parameters
stateState to use
qlQuote level
Return values
trueSpaces should be added

Determines whether to add a trailing space to quotes: >>> foo as opposed to >>>foo

Definition at line 110 of file rfc3676.c.

111{
112 if (state->flags & STATE_REPLYING)
113 return false;
114
115 if (space_quotes(state))
116 return false;
117
118 if (!ql && !state->prefix)
119 return false;
120
121 /* The prefix will add its own space */
122 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
123 if (!c_text_flowed && !ql && state->prefix)
124 return false;
125
126 return true;
127}
static int space_quotes(struct State *state)
Should we add spaces between quote levels.
Definition: rfc3676.c:87
const char * prefix
String to add to the beginning of each output line.
Definition: state.h:51
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ print_indent()

static size_t print_indent ( int  ql,
struct State state,
int  add_suffix 
)
static

Print indented text.

Parameters
qlQuote level
stateState to work with
add_suffixIf true, write a trailing space character
Return values
numNumber of characters written

Definition at line 136 of file rfc3676.c.

137{
138 size_t wid = 0;
139
140 if (state->prefix)
141 {
142 /* use given prefix only for format=fixed replies to format=flowed,
143 * for format=flowed replies to format=flowed, use '>' indentation */
144 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
145 if (c_text_flowed)
146 {
147 ql++;
148 }
149 else
150 {
151 state_puts(state, state->prefix);
152 wid = mutt_strwidth(state->prefix);
153 }
154 }
155 for (int i = 0; i < ql; i++)
156 {
157 state_putc(state, '>');
158 if (space_quotes(state))
159 state_putc(state, ' ');
160 }
161 if (add_suffix)
162 state_putc(state, ' ');
163
164 if (space_quotes(state))
165 ql *= 2;
166
167 return ql + add_suffix + wid;
168}
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:443
#define state_puts(STATE, STR)
Definition: state.h:58
#define state_putc(STATE, STR)
Definition: state.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ flush_par()

static void flush_par ( struct State state,
struct FlowedState fst 
)
static

Write out the paragraph.

Parameters
stateState to work with
fstState of the flowed text

Definition at line 175 of file rfc3676.c.

176{
177 if (fst->width > 0)
178 {
179 state_putc(state, '\n');
180 fst->width = 0;
181 }
182 fst->spaces = 0;
183}
size_t width
Definition: rfc3676.c:53
size_t spaces
Definition: rfc3676.c:54
+ Here is the caller graph for this function:

◆ quote_width()

static int quote_width ( struct State state,
int  ql 
)
static

Calculate the paragraph width based upon the quote level.

Parameters
stateState to use
qlQuote level
Return values
numParagraph width

The start of a quoted line will be ">>> ", so we need to subtract the space required for the prefix from the terminal width.

Definition at line 194 of file rfc3676.c.

195{
196 const int screen_width = (state->flags & STATE_DISPLAY) ? state->wraplen : 80;
197 const short c_reflow_wrap = cs_subset_number(NeoMutt->sub, "reflow_wrap");
198 int width = mutt_window_wrap_cols(screen_width, c_reflow_wrap);
199 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
200 if (c_text_flowed && (state->flags & STATE_REPLYING))
201 {
202 /* When replying, force a wrap at FLOWED_MAX to comply with RFC3676
203 * guidelines */
204 if (width > FLOWED_MAX)
205 width = FLOWED_MAX;
206 ql++; /* When replying, we will add an additional quote level */
207 }
208 /* adjust the paragraph width subtracting the number of prefix chars */
209 width -= space_quotes(state) ? ql * 2 : ql;
210 /* When displaying (not replying), there may be a space between the prefix
211 * string and the paragraph */
212 if (add_quote_suffix(state, ql))
213 width--;
214 /* failsafe for really long quotes */
215 if (width <= 0)
216 width = FLOWED_MAX; /* arbitrary, since the line will wrap */
217 return width;
218}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
#define STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:33
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:372
#define FLOWED_MAX
Definition: rfc3676.c:46
static bool add_quote_suffix(struct State *state, int ql)
Should we add a trailing space to quotes.
Definition: rfc3676.c:110
int wraplen
Width to wrap lines to (when flags & STATE_DISPLAY)
Definition: state.h:53
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ print_flowed_line()

static void print_flowed_line ( char *  line,
struct State state,
int  ql,
struct FlowedState fst,
bool  term 
)
static

Print a format-flowed line.

Parameters
lineText to print
stateState to work with
qlQuote level
fstState of the flowed text
termIf true, terminate with a new line

Definition at line 228 of file rfc3676.c.

230{
231 size_t width, w, words = 0;
232 char *p = NULL;
233 char last;
234
235 if (!line || (*line == '\0'))
236 {
237 /* flush current paragraph (if any) first */
238 flush_par(state, fst);
239 print_indent(ql, state, 0);
240 state_putc(state, '\n');
241 return;
242 }
243
244 width = quote_width(state, ql);
245 last = line[mutt_str_len(line) - 1];
246
247 mutt_debug(LL_DEBUG5, "f=f: line [%s], width = %ld, spaces = %zu\n", line,
248 (long) width, fst->spaces);
249
250 for (words = 0; (p = mutt_str_sep(&line, " "));)
251 {
252 mutt_debug(LL_DEBUG5, "f=f: word [%s], width: %zu, remaining = [%s]\n", p,
253 fst->width, line);
254
255 /* remember number of spaces */
256 if (*p == '\0')
257 {
258 mutt_debug(LL_DEBUG3, "f=f: additional space\n");
259 fst->spaces++;
260 continue;
261 }
262 /* there's exactly one space prior to every but the first word */
263 if (words)
264 fst->spaces++;
265
266 w = mutt_strwidth(p);
267 /* see if we need to break the line but make sure the first word is put on
268 * the line regardless; if for DelSp=yes only one trailing space is used,
269 * we probably have a long word that we should break within (we leave that
270 * up to the pager or user) */
271 if (!(!fst->spaces && fst->delsp && (last != ' ')) && (w < width) &&
272 (w + fst->width + fst->spaces > width))
273 {
274 mutt_debug(LL_DEBUG3, "f=f: break line at %zu, %zu spaces left\n",
275 fst->width, fst->spaces);
276 /* only honor trailing spaces for format=flowed replies */
277 const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
278 if (c_text_flowed)
279 for (; fst->spaces; fst->spaces--)
280 state_putc(state, ' ');
281 state_putc(state, '\n');
282 fst->width = 0;
283 fst->spaces = 0;
284 words = 0;
285 }
286
287 if (!words && !fst->width)
288 fst->width = print_indent(ql, state, add_quote_suffix(state, ql));
289 fst->width += w + fst->spaces;
290 for (; fst->spaces; fst->spaces--)
291 state_putc(state, ' ');
292 state_puts(state, p);
293 words++;
294 }
295
296 if (term)
297 flush_par(state, fst);
298}
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:186
static size_t print_indent(int ql, struct State *state, int add_suffix)
Print indented text.
Definition: rfc3676.c:136
static int quote_width(struct State *state, int ql)
Calculate the paragraph width based upon the quote level.
Definition: rfc3676.c:194
static void flush_par(struct State *state, struct FlowedState *fst)
Write out the paragraph.
Definition: rfc3676.c:175
bool delsp
Definition: rfc3676.c:55
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ print_fixed_line()

static void print_fixed_line ( const char *  line,
struct State state,
int  ql,
struct FlowedState fst 
)
static

Print a fixed format line.

Parameters
lineText to print
stateState to work with
qlQuote level
fstState of the flowed text

Definition at line 307 of file rfc3676.c.

309{
310 print_indent(ql, state, add_quote_suffix(state, ql));
311 if (line && *line)
312 state_puts(state, line);
313 state_putc(state, '\n');
314
315 fst->width = 0;
316 fst->spaces = 0;
317}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc3676_is_format_flowed()

bool mutt_rfc3676_is_format_flowed ( struct Body b)

Is the Email "format-flowed"?

Parameters
bEmail Body to examine
Return values
trueEmail is "format-flowed"

Definition at line 394 of file rfc3676.c.

395{
396 if (b && (b->type == TYPE_TEXT) && mutt_istr_equal("plain", b->subtype))
397 {
398 const char *format = mutt_param_get(&b->parameter, "format");
399 if (mutt_istr_equal("flowed", format))
400 return true;
401 }
402
403 return false;
404}
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:672
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:85
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:63
char * subtype
content-type subtype
Definition: body.h:61
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rfc3676_space_stuff()

static void rfc3676_space_stuff ( const char *  filename,
bool  unstuff 
)
static

Perform required RFC3676 space stuffing.

Parameters
filenameAttachment file
unstuffIf true, remove space stuffing

Space stuffing means that we have to add leading spaces to certain lines:

  • lines starting with a space
  • lines starting with 'From '

Care is taken to preserve the e->body->filename, as mutt -i -E can directly edit a passed in filename.

Definition at line 419 of file rfc3676.c.

420{
421 FILE *fp_out = NULL;
422 char *buf = NULL;
423 size_t blen = 0;
424
425 struct Buffer *tempfile = buf_pool_get();
426
427 FILE *fp_in = mutt_file_fopen(filename, "r");
428 if (!fp_in)
429 goto bail;
430
431 buf_mktemp(tempfile);
432 fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
433 if (!fp_out)
434 goto bail;
435
436 while ((buf = mutt_file_read_line(buf, &blen, fp_in, NULL, MUTT_RL_NO_FLAGS)) != NULL)
437 {
438 if (unstuff)
439 {
440 if (buf[0] == ' ')
441 fputs(buf + 1, fp_out);
442 else
443 fputs(buf, fp_out);
444 }
445 else
446 {
447 if ((buf[0] == ' ') || mutt_str_startswith(buf, "From "))
448 fputc(' ', fp_out);
449 fputs(buf, fp_out);
450 }
451 fputc('\n', fp_out);
452 }
453 FREE(&buf);
454 mutt_file_fclose(&fp_in);
455 mutt_file_fclose(&fp_out);
456 mutt_file_set_mtime(filename, buf_string(tempfile));
457
458 fp_in = mutt_file_fopen(buf_string(tempfile), "r");
459 if (!fp_in)
460 goto bail;
461
462 if ((truncate(filename, 0) == -1) || ((fp_out = mutt_file_fopen(filename, "a")) == NULL))
463 {
464 mutt_perror("%s", filename);
465 goto bail;
466 }
467
468 mutt_file_copy_stream(fp_in, fp_out);
469 mutt_file_set_mtime(buf_string(tempfile), filename);
470 unlink(buf_string(tempfile));
471
472bail:
473 mutt_file_fclose(&fp_in);
474 mutt_file_fclose(&fp_out);
475 buf_pool_release(&tempfile);
476}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:287
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
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:1068
#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
#define mutt_perror(...)
Definition: logging2.h:93
#define FREE(x)
Definition: memory.h:55
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
String manipulation buffer.
Definition: buffer.h:36
#define buf_mktemp(buf)
Definition: tmp.h:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc3676_space_stuff()

void mutt_rfc3676_space_stuff ( struct Email e)

Perform RFC3676 space stuffing on an Email.

Parameters
eEmail
Note
We don't check the option $text_flowed because we want to stuff based the actual content type. The option only decides whether to set format=flowed on new messages.

Definition at line 486 of file rfc3676.c.

487{
488 if (!e || !e->body || !e->body->filename)
489 return;
490
493}
static void rfc3676_space_stuff(const char *filename, bool unstuff)
Perform required RFC3676 space stuffing.
Definition: rfc3676.c:419
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:394
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:59
struct Body * body
List of MIME parts.
Definition: email.h:69
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc3676_space_unstuff()

void mutt_rfc3676_space_unstuff ( struct Email e)

Remove RFC3676 space stuffing.

Parameters
eEmail

Definition at line 499 of file rfc3676.c.

500{
501 if (!e || !e->body || !e->body->filename)
502 return;
503
506}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc3676_space_unstuff_attachment()

void mutt_rfc3676_space_unstuff_attachment ( struct Body b,
const char *  filename 
)

Unstuff attachments.

Parameters
bEmail Body (OPTIONAL)
filenameAttachment file

This routine is used when saving/piping/viewing rfc3676 attachments.

If b is provided, the function will verify that the Email is format-flowed. The filename will be unstuffed, not b->filename or b->fp.

Definition at line 518 of file rfc3676.c.

519{
520 if (!filename)
521 return;
522
524 return;
525
526 rfc3676_space_stuff(filename, true);
527}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_rfc3676_space_stuff_attachment()

void mutt_rfc3676_space_stuff_attachment ( struct Body b,
const char *  filename 
)

Stuff attachments.

Parameters
bEmail Body (OPTIONAL)
filenameAttachment file

This routine is used when filtering rfc3676 attachments.

If b is provided, the function will verify that the Email is format-flowed. The filename will be unstuffed, not b->filename or b->fp.

Definition at line 539 of file rfc3676.c.

540{
541 if (!filename)
542 return;
543
545 return;
546
547 rfc3676_space_stuff(filename, false);
548}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: