NeoMutt  2020-04-24
Teaching an old dog new tricks
DOXYGEN
edit.c
Go to the documentation of this file.
1 
30 /* Close approximation of the mailx(1) builtin editor for sending mail. */
31 
32 #include "config.h"
33 #include <ctype.h>
34 #include <errno.h>
35 #include <locale.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include "mutt/lib.h"
41 #include "address/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "gui/lib.h"
45 #include "mutt.h"
46 #include "edit.h"
47 #include "alias.h"
48 #include "context.h"
49 #include "globals.h"
50 #include "hdrline.h"
51 #include "mutt_header.h"
52 #include "muttlib.h"
53 #include "protos.h"
54 
55 /* These Config Variables are only used in edit.c */
56 char *C_Escape;
57 
58 /* SLcurses_waddnstr() can't take a "const char *", so this is only
59  * declared "static" (sigh)
60  */
61 static char *EditorHelp1 =
62  N_("~~ insert a line beginning with a single ~\n"
63  "~b users add users to the Bcc: field\n"
64  "~c users add users to the Cc: field\n"
65  "~f messages include messages\n"
66  "~F messages same as ~f, except also include headers\n"
67  "~h edit the message header\n"
68  "~m messages include and quote messages\n"
69  "~M messages same as ~m, except include headers\n"
70  "~p print the message\n");
71 
72 static char *EditorHelp2 =
73  N_("~q write file and quit editor\n"
74  "~r file read a file into the editor\n"
75  "~t users add users to the To: field\n"
76  "~u recall the previous line\n"
77  "~v edit message with the $visual editor\n"
78  "~w file write message to file\n"
79  "~x abort changes and quit editor\n"
80  "~? this message\n"
81  ". on a line by itself ends input\n");
82 
94 static char **be_snarf_data(FILE *fp, char **buf, int *bufmax, int *buflen,
95  LOFF_T offset, int bytes, int prefix)
96 {
97  char tmp[8192];
98  char *p = tmp;
99  int tmplen = sizeof(tmp);
100 
101  tmp[sizeof(tmp) - 1] = '\0';
102  if (prefix)
103  {
104  mutt_str_strfcpy(tmp, C_IndentString, sizeof(tmp));
105  tmplen = mutt_str_strlen(tmp);
106  p = tmp + tmplen;
107  tmplen = sizeof(tmp) - tmplen;
108  }
109 
110  fseeko(fp, offset, SEEK_SET);
111  while (bytes > 0)
112  {
113  if (!fgets(p, tmplen - 1, fp))
114  break;
115  bytes -= mutt_str_strlen(p);
116  if (*bufmax == *buflen)
117  mutt_mem_realloc(&buf, sizeof(char *) * (*bufmax += 25));
118  buf[(*buflen)++] = mutt_str_strdup(tmp);
119  }
120  if (buf && (*bufmax == *buflen))
121  { /* Do not smash memory past buf */
122  mutt_mem_realloc(&buf, sizeof(char *) * (++*bufmax));
123  }
124  if (buf)
125  buf[*buflen] = NULL;
126  return buf;
127 }
128 
138 static char **be_snarf_file(const char *path, char **buf, int *max, int *len, bool verbose)
139 {
140  char tmp[1024];
141  struct stat sb;
142 
143  FILE *fp = fopen(path, "r");
144  if (fp)
145  {
146  fstat(fileno(fp), &sb);
147  buf = be_snarf_data(fp, buf, max, len, 0, sb.st_size, 0);
148  if (verbose)
149  {
150  snprintf(tmp, sizeof(tmp), "\"%s\" %lu bytes\n", path, (unsigned long) sb.st_size);
151  mutt_window_addstr(tmp);
152  }
153  mutt_file_fclose(&fp);
154  }
155  else
156  {
157  snprintf(tmp, sizeof(tmp), "%s: %s\n", path, strerror(errno));
158  mutt_window_addstr(tmp);
159  }
160  return buf;
161 }
162 
171 static int be_barf_file(const char *path, char **buf, int buflen)
172 {
173  FILE *fp = fopen(path, "w");
174  if (!fp)
175  {
176  mutt_window_addstr(strerror(errno));
177  mutt_window_addch('\n');
178  return -1;
179  }
180  for (int i = 0; i < buflen; i++)
181  fputs(buf[i], fp);
182  if (mutt_file_fclose(&fp) == 0)
183  return 0;
184  mutt_window_printf("fclose: %s\n", strerror(errno));
185  return -1;
186 }
187 
193 static void be_free_memory(char **buf, int buflen)
194 {
195  while (buflen-- > 0)
196  FREE(&buf[buflen]);
197  FREE(&buf);
198 }
199 
210 static char **be_include_messages(char *msg, char **buf, int *bufmax,
211  int *buflen, int pfx, int inc_hdrs)
212 {
213  int n;
214  // int offset, bytes;
215  char tmp[1024];
216 
217  if (!msg || !buf || !bufmax || !buflen)
218  return buf;
219 
220  while ((msg = strtok(msg, " ,")))
221  {
222  if ((mutt_str_atoi(msg, &n) == 0) && (n > 0) && (n <= Context->mailbox->msg_count))
223  {
224  n--;
225 
226  /* add the attribution */
227  if (C_Attribution)
228  {
229  setlocale(LC_TIME, NONULL(C_AttributionLocale));
230  mutt_make_string(tmp, sizeof(tmp) - 1, 0, C_Attribution, Context,
232  setlocale(LC_TIME, "");
233  strcat(tmp, "\n");
234  }
235 
236  if (*bufmax == *buflen)
237  mutt_mem_realloc(&buf, sizeof(char *) * (*bufmax += 25));
238  buf[(*buflen)++] = mutt_str_strdup(tmp);
239 
240 #if 0
241  /* This only worked for mbox Mailboxes because they had Context->fp set.
242  * As that no longer exists, the code is now completely broken. */
243  bytes = Context->mailbox->emails[n]->content->length;
244  if (inc_hdrs)
245  {
246  offset = Context->mailbox->emails[n]->offset;
247  bytes += Context->mailbox->emails[n]->content->offset - offset;
248  }
249  else
250  offset = Context->mailbox->emails[n]->content->offset;
251  buf = be_snarf_data(Context->fp, buf, bufmax, buflen, offset, bytes, pfx);
252 #endif
253 
254  if (*bufmax == *buflen)
255  mutt_mem_realloc(&buf, sizeof(char *) * (*bufmax += 25));
256  buf[(*buflen)++] = mutt_str_strdup("\n");
257  }
258  else
259  mutt_window_printf(_("%d: invalid message number.\n"), n);
260  msg = NULL;
261  }
262  return buf;
263 }
264 
269 static void be_print_header(struct Envelope *env)
270 {
271  char tmp[8192];
272 
273  if (!TAILQ_EMPTY(&env->to))
274  {
275  mutt_window_addstr("To: ");
276  tmp[0] = '\0';
277  mutt_addrlist_write(&env->to, tmp, sizeof(tmp), true);
278  mutt_window_addstr(tmp);
279  mutt_window_addch('\n');
280  }
281  if (!TAILQ_EMPTY(&env->cc))
282  {
283  mutt_window_addstr("Cc: ");
284  tmp[0] = '\0';
285  mutt_addrlist_write(&env->cc, tmp, sizeof(tmp), true);
286  mutt_window_addstr(tmp);
287  mutt_window_addch('\n');
288  }
289  if (!TAILQ_EMPTY(&env->bcc))
290  {
291  mutt_window_addstr("Bcc: ");
292  tmp[0] = '\0';
293  mutt_addrlist_write(&env->bcc, tmp, sizeof(tmp), true);
294  mutt_window_addstr(tmp);
295  mutt_window_addch('\n');
296  }
297  if (env->subject)
298  {
299  mutt_window_addstr("Subject: ");
301  mutt_window_addch('\n');
302  }
303  mutt_window_addch('\n');
304 }
305 
311 static void be_edit_header(struct Envelope *e, bool force)
312 {
313  char tmp[8192];
314 
316 
317  mutt_window_addstr("To: ");
318  tmp[0] = '\0';
320  mutt_addrlist_write(&e->to, tmp, sizeof(tmp), false);
321  if (TAILQ_EMPTY(&e->to) || force)
322  {
323  if (mutt_enter_string(tmp, sizeof(tmp), 4, MUTT_COMP_NO_FLAGS) == 0)
324  {
325  mutt_addrlist_clear(&e->to);
326  mutt_addrlist_parse2(&e->to, tmp);
327  mutt_expand_aliases(&e->to);
328  mutt_addrlist_to_intl(&e->to, NULL); /* XXX - IDNA error reporting? */
329  tmp[0] = '\0';
330  mutt_addrlist_write(&e->to, tmp, sizeof(tmp), true);
332  }
333  }
334  else
335  {
336  mutt_addrlist_to_intl(&e->to, NULL); /* XXX - IDNA error reporting? */
337  mutt_window_addstr(tmp);
338  }
339  mutt_window_addch('\n');
340 
341  if (!e->subject || force)
342  {
343  mutt_window_addstr("Subject: ");
344  mutt_str_strfcpy(tmp, e->subject ? e->subject : "", sizeof(tmp));
345  if (mutt_enter_string(tmp, sizeof(tmp), 9, MUTT_COMP_NO_FLAGS) == 0)
346  mutt_str_replace(&e->subject, tmp);
347  mutt_window_addch('\n');
348  }
349 
350  if ((TAILQ_EMPTY(&e->cc) && C_Askcc) || force)
351  {
352  mutt_window_addstr("Cc: ");
353  tmp[0] = '\0';
355  mutt_addrlist_write(&e->cc, tmp, sizeof(tmp), false);
356  if (mutt_enter_string(tmp, sizeof(tmp), 4, MUTT_COMP_NO_FLAGS) == 0)
357  {
358  mutt_addrlist_clear(&e->cc);
359  mutt_addrlist_parse2(&e->cc, tmp);
360  mutt_expand_aliases(&e->cc);
361  tmp[0] = '\0';
362  mutt_addrlist_to_intl(&e->cc, NULL);
363  mutt_addrlist_write(&e->cc, tmp, sizeof(tmp), true);
365  }
366  else
367  mutt_addrlist_to_intl(&e->cc, NULL);
368  mutt_window_addch('\n');
369  }
370 
371  if (C_Askbcc || force)
372  {
373  mutt_window_addstr("Bcc: ");
374  tmp[0] = '\0';
376  mutt_addrlist_write(&e->bcc, tmp, sizeof(tmp), false);
377  if (mutt_enter_string(tmp, sizeof(tmp), 5, MUTT_COMP_NO_FLAGS) == 0)
378  {
380  mutt_addrlist_parse2(&e->bcc, tmp);
382  mutt_addrlist_to_intl(&e->bcc, NULL);
383  tmp[0] = '\0';
384  mutt_addrlist_write(&e->bcc, tmp, sizeof(tmp), true);
386  }
387  else
388  mutt_addrlist_to_intl(&e->bcc, NULL);
389  mutt_window_addch('\n');
390  }
391 }
392 
401 int mutt_builtin_editor(const char *path, struct Email *e_new, struct Email *e_cur)
402 {
403  char **buf = NULL;
404  int bufmax = 0, buflen = 0;
405  char tmp[1024];
406  bool abort = false;
407  bool done = false;
408  char *p = NULL;
409 
410  scrollok(stdscr, true);
411 
412  be_edit_header(e_new->env, false);
413 
414  mutt_window_addstr(_("(End message with a . on a line by itself)\n"));
415 
416  buf = be_snarf_file(path, buf, &bufmax, &buflen, false);
417 
418  tmp[0] = '\0';
419  while (!done)
420  {
421  if (mutt_enter_string(tmp, sizeof(tmp), 0, MUTT_COMP_NO_FLAGS) == -1)
422  {
423  tmp[0] = '\0';
424  continue;
425  }
426  mutt_window_addch('\n');
427 
428  if (C_Escape && (tmp[0] == C_Escape[0]) && (tmp[1] != C_Escape[0]))
429  {
430  /* remove trailing whitespace from the line */
431  p = tmp + mutt_str_strlen(tmp) - 1;
432  while ((p >= tmp) && IS_SPACE(*p))
433  *p-- = '\0';
434 
435  p = tmp + 2;
436  SKIPWS(p);
437 
438  switch (tmp[1])
439  {
440  case '?':
443  break;
444  case 'b':
445  mutt_addrlist_parse2(&e_new->env->bcc, p);
446  mutt_expand_aliases(&e_new->env->bcc);
447  break;
448  case 'c':
449  mutt_addrlist_parse2(&e_new->env->cc, p);
450  mutt_expand_aliases(&e_new->env->cc);
451  break;
452  case 'h':
453  be_edit_header(e_new->env, true);
454  break;
455  case 'F':
456  case 'f':
457  case 'm':
458  case 'M':
459  if (Context)
460  {
461  if (!*p && e_cur)
462  {
463  /* include the current message */
464  p = tmp + mutt_str_strlen(tmp) + 1;
465  snprintf(tmp + mutt_str_strlen(tmp),
466  sizeof(tmp) - mutt_str_strlen(tmp), " %d", e_cur->msgno + 1);
467  }
468  buf = be_include_messages(p, buf, &bufmax, &buflen, (tolower(tmp[1]) == 'm'),
469  (isupper((unsigned char) tmp[1])));
470  }
471  else
472  mutt_window_addstr(_("No mailbox.\n"));
473  break;
474  case 'p':
475  mutt_window_addstr("-----\n");
476  mutt_window_addstr(_("Message contains:\n"));
477  be_print_header(e_new->env);
478  for (int i = 0; i < buflen; i++)
479  mutt_window_addstr(buf[i]);
480  /* L10N: This entry is shown AFTER the message content,
481  not IN the middle of the content. So it doesn't mean "(message
482  will continue)" but means "(press any key to continue using
483  neomutt)". */
484  mutt_window_addstr(_("(continue)\n"));
485  break;
486  case 'q':
487  done = true;
488  break;
489  case 'r':
490  if (*p)
491  {
492  struct Buffer *filename = mutt_buffer_pool_get();
493  mutt_buffer_strcpy(filename, p);
494  mutt_buffer_expand_path(filename);
495  buf = be_snarf_file(mutt_b2s(filename), buf, &bufmax, &buflen, true);
496  mutt_buffer_pool_release(&filename);
497  }
498  else
499  mutt_window_addstr(_("missing filename.\n"));
500  break;
501  case 's':
502  mutt_str_replace(&e_new->env->subject, p);
503  break;
504  case 't':
505  mutt_addrlist_parse(&e_new->env->to, p);
506  mutt_expand_aliases(&e_new->env->to);
507  break;
508  case 'u':
509  if (buflen)
510  {
511  buflen--;
512  mutt_str_strfcpy(tmp, buf[buflen], sizeof(tmp));
513  tmp[mutt_str_strlen(tmp) - 1] = '\0';
514  FREE(&buf[buflen]);
515  buf[buflen] = NULL;
516  continue;
517  }
518  else
519  mutt_window_addstr(_("No lines in message.\n"));
520  break;
521 
522  case 'e':
523  case 'v':
524  if (be_barf_file(path, buf, buflen) != 0)
525  break;
526 
527  const char *tag = NULL;
528  char *err = NULL;
529  be_free_memory(buf, buflen);
530  buf = NULL;
531  bufmax = 0;
532  buflen = 0;
533 
534  if (C_EditHeaders)
535  {
536  mutt_env_to_local(e_new->env);
537  mutt_edit_headers(NONULL(C_Visual), path, e_new, NULL);
538  if (mutt_env_to_intl(e_new->env, &tag, &err))
539  mutt_window_printf(_("Bad IDN in '%s': '%s'"), tag, err);
540  /* tag is a statically allocated string and should not be freed */
541  FREE(&err);
542  }
543  else
545 
546  buf = be_snarf_file(path, buf, &bufmax, &buflen, false);
547 
548  mutt_window_addstr(_("(continue)\n"));
549  break;
550  case 'w':
551  be_barf_file((p[0] != '\0') ? p : path, buf, buflen);
552  break;
553  case 'x':
554  abort = true;
555  done = true;
556  break;
557  default:
558  mutt_window_printf(_("%s: unknown editor command (~? for help)\n"), tmp);
559  break;
560  }
561  }
562  else if (mutt_str_strcmp(".", tmp) == 0)
563  done = true;
564  else
565  {
566  mutt_str_strcat(tmp, sizeof(tmp), "\n");
567  if (buflen == bufmax)
568  mutt_mem_realloc(&buf, sizeof(char *) * (bufmax += 25));
569  buf[buflen++] = mutt_str_strdup((tmp[1] == '~') ? tmp + 1 : tmp);
570  }
571 
572  tmp[0] = '\0';
573  }
574 
575  if (!abort)
576  be_barf_file(path, buf, buflen);
577  be_free_memory(buf, buflen);
578 
579  return abort ? -1 : 0;
580 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
The "current" mailbox.
Definition: context.h:37
#define NONULL(x)
Definition: string2.h:37
Representation of the email&#39;s header.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:303
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:262
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
WHERE char * C_AttributionLocale
Config: Locale for dates in the attribution message.
Definition: globals.h:100
WHERE char * C_IndentString
Config: String used to indent &#39;reply&#39; text.
Definition: globals.h:136
Structs that make up an email.
String processing routines to generate the mail index.
The "currently-open" mailbox.
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1329
struct AddressList bcc
Email&#39;s &#39;Bcc&#39; list.
Definition: envelope.h:60
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1412
#define mutt_make_string(BUF, BUFLEN, COLS, S, CTX, M, E)
Definition: hdrline.h:61
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Body * content
List of MIME parts.
Definition: email.h:90
String manipulation buffer.
Definition: buffer.h:33
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
#define _(a)
Definition: message.h:28
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:692
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:613
int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err)
Convert an Envelope&#39;s Address fields to Punycode format.
Definition: envelope.c:309
static char ** be_snarf_data(FILE *fp, char **buf, int *bufmax, int *buflen, LOFF_T offset, int bytes, int prefix)
Read data from a file into a buffer.
Definition: edit.c:94
Representation of a single alias to an email address.
Hundreds of global variables to back the user variables.
Email Address Handling.
Some miscellaneous functions.
static void be_edit_header(struct Envelope *e, bool force)
Edit the message headers.
Definition: edit.c:311
WHERE bool C_EditHeaders
Config: Let the user edit the email headers whilst editing an email.
Definition: globals.h:212
struct Mailbox * mailbox
Definition: context.h:51
Many unsorted constants and some structs.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
#define SKIPWS(ch)
Definition: string2.h:47
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define mutt_b2s(buf)
Definition: buffer.h:41
Prototypes for many functions.
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
static char ** be_snarf_file(const char *path, char **buf, int *max, int *len, bool verbose)
Read a file into a buffer.
Definition: edit.c:138
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:776
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:455
int mutt_window_mvaddstr(struct MuttWindow *win, int row, int col, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:292
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:278
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:83
static char ** be_include_messages(char *msg, char **buf, int *bufmax, int *buflen, int pfx, int inc_hdrs)
Gather the contents of some messages.
Definition: edit.c:210
#define IS_SPACE(ch)
Definition: string2.h:38
char * mutt_str_strcat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:395
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope&#39;s Address fields to local format.
Definition: envelope.c:271
int mutt_window_printf(const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:458
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition: mutt_header.c:168
static char * EditorHelp2
Definition: edit.c:72
int mutt_window_addch(int ch)
Write one character to a Window.
Definition: mutt_window.c:400
char * C_Escape
Config: Escape character to use for functions in the built-in editor.
Definition: edit.c:56
GUI basic built-in text editor.
char * subject
Email&#39;s subject.
Definition: envelope.h:66
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:327
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1247
int n
Definition: acutest.h:492
static void be_print_header(struct Envelope *env)
Print a message Header.
Definition: edit.c:269
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
int mutt_builtin_editor(const char *path, struct Email *e_new, struct Email *e_cur)
Show the user the built-in editor.
Definition: edit.c:401
int mutt_enter_string(char *buf, size_t buflen, int col, CompletionFlags flags)
Ask the user for a string.
Definition: enter.c:145
#define FREE(x)
Definition: memory.h:40
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct MuttWindow * MuttMessageWindow
Message Window.
Definition: mutt_window.c:46
int mutt_window_addstr(const char *str)
Write a string to a Window.
Definition: mutt_window.c:430
#define TAILQ_EMPTY(head)
Definition: queue.h:714
WHERE char * C_Attribution
Config: Message to start a reply, "On DATE, PERSON wrote:".
Definition: globals.h:99
Convenience wrapper for the library headers.
#define N_(a)
Definition: message.h:32
WHERE bool C_Askbcc
Config: Ask the user for the blind-carbon-copy recipients.
Definition: globals.h:197
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:353
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1144
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:641
static int be_barf_file(const char *path, char **buf, int buflen)
Write a buffer to a file.
Definition: edit.c:171
WHERE bool C_Askcc
Config: Ask the user for the carbon-copy recipients.
Definition: globals.h:198
The header of an Email.
Definition: envelope.h:54
int msgno
Number displayed to the user.
Definition: email.h:86
WHERE char * C_Visual
Config: Editor to use when &#39;~v&#39; is given in the built-in editor.
Definition: globals.h:149
static char * EditorHelp1
Definition: edit.c:61
static void be_free_memory(char **buf, int buflen)
Free an array of buffers.
Definition: edit.c:193