NeoMutt  2025-12-11-980-ge38c27
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 "module_data.h"
50#include "muttlib.h"
51#ifdef ENABLE_NLS
52#include <libintl.h>
53#endif
54
55#define MAX_ERRS 128
56
64int source_rc(const char *rcfile_path, struct ParseContext *pc, struct ParseError *pe)
65{
66 if (!rcfile_path || !pc || !pe)
67 return -1;
68
70 struct Buffer *err = pe->message;
71
72 int lineno = 0;
73 int rc = 0;
74 int 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 = 0;
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(&mod_data->muttrc_stack);
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, &mod_data->muttrc_stack, 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_NONE);
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(&mod_data->muttrc_stack))
212 {
213 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
214 STAILQ_REMOVE_HEAD(&mod_data->muttrc_stack, entries);
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_NONE) != 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
275{
277 if (mod_data)
278 mutt_list_free(&mod_data->muttrc_stack);
279}
280
289enum CommandResult parse_rc_line_cwd(const char *line, char *cwd,
290 struct ParseContext *pc, struct ParseError *pe)
291{
292 if (!line || !cwd || !pc || !pe)
293 return MUTT_CMD_ERROR;
294
297
298 struct Buffer *buf = buf_pool_get();
299 buf_strcpy(buf, line);
300 enum CommandResult ret = parse_rc_line(buf, pc, pe);
301 buf_pool_release(&buf);
302
303 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
304 STAILQ_REMOVE_HEAD(&mod_data->muttrc_stack, entries);
305 FREE(&np->data);
306 FREE(&np);
307
308 return ret;
309}
310
318{
320 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
321 if (np && np->data)
322 return mutt_str_dup(np->data);
323
324 // stack is empty, return our own dummy file relative to cwd
325 struct Buffer *cwd = buf_pool_get();
326 mutt_path_getcwd(cwd);
327 buf_addstr(cwd, "/dummy.rc");
328 char *ret = buf_strdup(cwd);
329 buf_pool_release(&cwd);
330 return ret;
331}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:607
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
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
Commands private Module data.
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 MoreArgs(buf)
Definition extract.h:32
@ TOKEN_NONE
No flags are set.
Definition extract.h:50
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:678
#define mutt_file_fclose(FP)
Definition file.h:144
@ MUTT_RL_CONT
-continuation
Definition file.h:44
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
@ MODULE_ID_COMMANDS
ModuleCommands, NeoMutt Commands
Definition module_api.h:54
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
#define MUTT_ICONV_NONE
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:231
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:666
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:587
#define PATH_MAX
Definition mutt.h:49
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition muttlib.c:645
Some miscellaneous functions.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
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_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:289
#define MAX_ERRS
Definition source.c:55
void source_stack_cleanup(void)
Free memory from the stack used for the source command.
Definition source.c:274
int source_rc(const char *rcfile_path, struct ParseContext *pc, struct ParseError *pe)
Read an initialization file.
Definition source.c:64
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition source.c:317
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:162
Commands private Module data.
Definition module_data.h:32
struct ListHead muttrc_stack
LIFO of sourced config files (avoid cycles)
Definition module_data.h:34
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