NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
source.c
Go to the documentation of this file.
1
30
36
37#include "config.h"
38#include <errno.h>
39#include <limits.h>
40#include <stdbool.h>
41#include <stdio.h>
42#include <string.h>
43#include <sys/types.h>
44#include "mutt/lib.h"
45#include "config/lib.h"
46#include "core/lib.h"
47#include "source.h"
48#include "parse/lib.h"
49#include "muttlib.h"
50#ifdef ENABLE_NLS
51#include <libintl.h>
52#endif
53
57
58#define MAX_ERRS 128
59
67int source_rc(const char *rcfile_path, struct ParseContext *pc, struct ParseError *pe)
68{
69 if (!rcfile_path || !pc || !pe)
70 return -1;
71
72 struct Buffer *err = pe->message;
73
74 int lineno = 0, rc = 0, warnings = 0;
75 enum CommandResult line_rc;
76 struct Buffer *linebuf = NULL;
77 char *line = NULL;
78 char *currentline = NULL;
79 char rcfile[PATH_MAX + 1] = { 0 };
80 size_t linelen = 0;
81 pid_t pid;
82
83 mutt_str_copy(rcfile, rcfile_path, sizeof(rcfile));
84
85 size_t rcfilelen = mutt_str_len(rcfile);
86 if (rcfilelen == 0)
87 return -1;
88
89 bool ispipe = rcfile[rcfilelen - 1] == '|';
90
91 if (!ispipe)
92 {
93 struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
94 if (!mutt_path_to_absolute(rcfile, np ? NONULL(np->data) : ""))
95 {
96 mutt_error(_("Error: Can't build path of '%s'"), rcfile_path);
97 return -1;
98 }
99
100 STAILQ_FOREACH(np, &MuttrcStack, entries)
101 {
102 if (mutt_str_equal(np->data, rcfile))
103 {
104 break;
105 }
106 }
107 if (np)
108 {
109 mutt_error(_("Error: Cyclic sourcing of configuration file '%s'"), rcfile);
110 return -1;
111 }
112
114 }
115
116 mutt_debug(LL_DEBUG2, "Reading configuration file '%s'\n", rcfile);
117
118 FILE *fp = mutt_open_read(rcfile, &pid);
119 if (!fp)
120 {
121 buf_printf(err, "%s: %s", rcfile, strerror(errno));
122 return -1;
123 }
124
125 linebuf = buf_pool_get();
126
127 const char *const c_config_charset = cs_subset_string(NeoMutt->sub, "config_charset");
128 const char *const c_charset = cc_charset();
129 while ((line = mutt_file_read_line(line, &linelen, fp, &lineno, MUTT_RL_CONT)) != NULL)
130 {
131 const bool conv = c_config_charset && c_charset;
132 if (conv)
133 {
134 currentline = mutt_str_dup(line);
135 if (!currentline)
136 continue;
137 mutt_ch_convert_string(&currentline, c_config_charset, c_charset, MUTT_ICONV_NO_FLAGS);
138 }
139 else
140 {
141 currentline = line;
142 }
143
144 buf_strcpy(linebuf, currentline);
145
146 buf_reset(err);
147 line_rc = parse_rc_line(linebuf, pc, pe);
148 if (line_rc == MUTT_CMD_ERROR)
149 {
150 mutt_error("%s:%d: %s", rcfile, lineno, buf_string(err));
151 if (--rc < -MAX_ERRS)
152 {
153 if (conv)
154 FREE(&currentline);
155 break;
156 }
157 }
158 else if (line_rc == MUTT_CMD_WARNING)
159 {
160 /* Warning */
161 mutt_warning("%s:%d: %s", rcfile, lineno, buf_string(err));
162 warnings++;
163 }
164 else if (line_rc == MUTT_CMD_FINISH)
165 {
166 if (conv)
167 FREE(&currentline);
168 break; /* Found "finish" command */
169 }
170 else
171 {
172 if (rc < 0)
173 rc = -1;
174 }
175 if (conv)
176 FREE(&currentline);
177 }
178
179 FREE(&line);
180 mutt_file_fclose(&fp);
181 if (ispipe && (pid != -1))
182 {
183 int status = filter_wait(pid);
184 if (status != 0)
185 {
186 mutt_error(_("Command '%s' exited with status %d"), rcfile, status);
187 if (rc == 0)
188 rc = -1; // Set error code if not already set
189 }
190 }
191
192 if (rc)
193 {
194 /* the neomuttrc source keyword */
195 buf_reset(err);
196 buf_printf(err, (rc >= -MAX_ERRS) ? _("source: errors in %s") : _("source: reading aborted due to too many errors in %s"),
197 rcfile);
198 rc = -1;
199 }
200 else
201 {
202 /* Don't alias errors with warnings */
203 if (warnings > 0)
204 {
205 buf_printf(err, ngettext("source: %d warning in %s", "source: %d warnings in %s", warnings),
206 warnings, rcfile);
207 rc = -2;
208 }
209 }
210
211 if (!ispipe && !STAILQ_EMPTY(&MuttrcStack))
212 {
213 struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
215 FREE(&np->data);
216 FREE(&np);
217 }
218
219 buf_pool_release(&linebuf);
220 return rc;
221}
222
229enum CommandResult parse_source(const struct Command *cmd, struct Buffer *line,
230 const struct ParseContext *pc, struct ParseError *pe)
231{
232 struct Buffer *err = pe->message;
233
234 if (!MoreArgs(line))
235 {
236 buf_printf(err, _("%s: too few arguments"), cmd->name);
237 return MUTT_CMD_WARNING;
238 }
239
240 struct Buffer *token = buf_pool_get();
241 struct Buffer *path = buf_pool_get();
243
244 do
245 {
246 if (parse_extract_token(token, line, TOKEN_BACKTICK_VARS) != 0)
247 {
248 buf_printf(err, _("source: error at %s"), line->dptr);
249 goto done;
250 }
251 buf_copy(path, token);
252 expand_path(path, false);
253
254 // Cheat: Remove the `const` so we can recurse
255 if (source_rc(buf_string(path), (struct ParseContext *) pc, pe) < 0)
256 {
257 buf_printf(err, _("source: file %s could not be sourced"), buf_string(path));
258 goto done;
259 }
260
261 } while (MoreArgs(line));
262
263 rc = MUTT_CMD_SUCCESS;
264
265done:
266 buf_pool_release(&path);
267 buf_pool_release(&token);
268 return rc;
269}
270
278
287enum CommandResult parse_rc_line_cwd(const char *line, char *cwd,
288 struct ParseContext *pc, struct ParseError *pe)
289{
290 if (!line || !cwd || !pc || !pe)
291 return MUTT_CMD_ERROR;
292
294
295 struct Buffer *buf = buf_pool_get();
296 buf_strcpy(buf, line);
297 enum CommandResult ret = parse_rc_line(buf, pc, pe);
298 buf_pool_release(&buf);
299
300 struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
302 FREE(&np->data);
303 FREE(&np);
304
305 return ret;
306}
307
315{
316 struct ListNode *np = STAILQ_FIRST(&MuttrcStack);
317 if (np && np->data)
318 return mutt_str_dup(np->data);
319
320 // stack is empty, return our own dummy file relative to cwd
321 struct Buffer *cwd = buf_pool_get();
322 mutt_path_getcwd(cwd);
323 buf_addstr(cwd, "/dummy.rc");
324 char *ret = buf_strdup(cwd);
325 buf_pool_release(&cwd);
326 return ret;
327}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:601
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
CommandResult
Error codes for command_t parse functions.
Definition command.h:37
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition command.h:40
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition command.h:38
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition command.h:39
@ MUTT_CMD_FINISH
Finish: Stop processing this file.
Definition command.h:41
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
Convenience wrapper for the config headers.
const char * cc_charset(void)
Get the cached value of $charset.
Convenience wrapper for the core headers.
int parse_extract_token(struct Buffer *dest, struct Buffer *line, TokenFlags flags)
Extract one token from a string.
Definition extract.c:49
#define TOKEN_BACKTICK_VARS
Expand variables within backticks.
Definition extract.h:53
#define MoreArgs(buf)
Definition extract.h:31
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:682
#define MUTT_RL_CONT
-continuation
Definition file.h:41
#define mutt_file_fclose(FP)
Definition file.h:139
enum CommandResult parse_source(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse the 'source' command - Implements Command::parse() -.
Definition source.c:229
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:817
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition charset.h:66
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:220
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool mutt_path_to_absolute(char *path, const char *reference)
Convert a relative path to its absolute form.
Definition path.c:333
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition path.c:476
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:583
#define PATH_MAX
Definition mutt.h:49
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:121
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition muttlib.c:644
Some miscellaneous functions.
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define STAILQ_REMOVE_HEAD(head, field)
Definition queue.h:461
#define STAILQ_HEAD_INITIALIZER(head)
Definition queue.h:324
#define STAILQ_FIRST(head)
Definition queue.h:388
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define STAILQ_EMPTY(head)
Definition queue.h:382
enum CommandResult parse_rc_line(struct Buffer *line, struct ParseContext *pc, struct ParseError *pe)
Parse a line of user config.
Definition rc.c:45
enum CommandResult parse_rc_line_cwd(const char *line, char *cwd, struct ParseContext *pc, struct ParseError *pe)
Parse and run a muttrc line in a relative directory.
Definition source.c:287
#define MAX_ERRS
Definition source.c:58
void source_stack_cleanup(void)
Free memory from the stack used for the source command.
Definition source.c:274
static struct ListHead MuttrcStack
LIFO designed to contain the list of config files that have been sourced and avoid cyclic sourcing.
Definition source.c:56
int source_rc(const char *rcfile_path, struct ParseContext *pc, struct ParseError *pe)
Read an initialization file.
Definition source.c:67
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition source.c:314
Parse Source Commands.
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
char * dptr
Current read/write position.
Definition buffer.h:38
const char * name
Name of the Command.
Definition command.h:159
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Context for config parsing (history/backtrace)
Definition pcontext.h:34
Detailed error information from config parsing.
Definition perror.h:34
struct Buffer * message
Error message.
Definition perror.h:35