NeoMutt  2025-12-11-911-gd8d604
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, rc = 0, warnings = 0;
73 enum CommandResult line_rc;
74 struct Buffer *linebuf = NULL;
75 char *line = NULL;
76 char *currentline = NULL;
77 char rcfile[PATH_MAX + 1] = { 0 };
78 size_t linelen = 0;
79 pid_t pid;
80
81 mutt_str_copy(rcfile, rcfile_path, sizeof(rcfile));
82
83 size_t rcfilelen = mutt_str_len(rcfile);
84 if (rcfilelen == 0)
85 return -1;
86
87 bool ispipe = rcfile[rcfilelen - 1] == '|';
88
89 if (!ispipe)
90 {
91 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
92 if (!mutt_path_to_absolute(rcfile, np ? NONULL(np->data) : ""))
93 {
94 mutt_error(_("Error: Can't build path of '%s'"), rcfile_path);
95 return -1;
96 }
97
98 STAILQ_FOREACH(np, &mod_data->muttrc_stack, entries)
99 {
100 if (mutt_str_equal(np->data, rcfile))
101 {
102 break;
103 }
104 }
105 if (np)
106 {
107 mutt_error(_("Error: Cyclic sourcing of configuration file '%s'"), rcfile);
108 return -1;
109 }
110
112 }
113
114 mutt_debug(LL_DEBUG2, "Reading configuration file '%s'\n", rcfile);
115
116 FILE *fp = mutt_open_read(rcfile, &pid);
117 if (!fp)
118 {
119 buf_printf(err, "%s: %s", rcfile, strerror(errno));
120 return -1;
121 }
122
123 linebuf = buf_pool_get();
124
125 const char *const c_config_charset = cs_subset_string(NeoMutt->sub, "config_charset");
126 const char *const c_charset = cc_charset();
127 while ((line = mutt_file_read_line(line, &linelen, fp, &lineno, MUTT_RL_CONT)) != NULL)
128 {
129 const bool conv = c_config_charset && c_charset;
130 if (conv)
131 {
132 currentline = mutt_str_dup(line);
133 if (!currentline)
134 continue;
135 mutt_ch_convert_string(&currentline, c_config_charset, c_charset, MUTT_ICONV_NONE);
136 }
137 else
138 {
139 currentline = line;
140 }
141
142 buf_strcpy(linebuf, currentline);
143
144 buf_reset(err);
145 line_rc = parse_rc_line(linebuf, pc, pe);
146 if (line_rc == MUTT_CMD_ERROR)
147 {
148 mutt_error("%s:%d: %s", rcfile, lineno, buf_string(err));
149 if (--rc < -MAX_ERRS)
150 {
151 if (conv)
152 FREE(&currentline);
153 break;
154 }
155 }
156 else if (line_rc == MUTT_CMD_WARNING)
157 {
158 /* Warning */
159 mutt_warning("%s:%d: %s", rcfile, lineno, buf_string(err));
160 warnings++;
161 }
162 else if (line_rc == MUTT_CMD_FINISH)
163 {
164 if (conv)
165 FREE(&currentline);
166 break; /* Found "finish" command */
167 }
168 else
169 {
170 if (rc < 0)
171 rc = -1;
172 }
173 if (conv)
174 FREE(&currentline);
175 }
176
177 FREE(&line);
178 mutt_file_fclose(&fp);
179 if (ispipe && (pid != -1))
180 {
181 int status = filter_wait(pid);
182 if (status != 0)
183 {
184 mutt_error(_("Command '%s' exited with status %d"), rcfile, status);
185 if (rc == 0)
186 rc = -1; // Set error code if not already set
187 }
188 }
189
190 if (rc)
191 {
192 /* the neomuttrc source keyword */
193 buf_reset(err);
194 buf_printf(err, (rc >= -MAX_ERRS) ? _("source: errors in %s") : _("source: reading aborted due to too many errors in %s"),
195 rcfile);
196 rc = -1;
197 }
198 else
199 {
200 /* Don't alias errors with warnings */
201 if (warnings > 0)
202 {
203 buf_printf(err, ngettext("source: %d warning in %s", "source: %d warnings in %s", warnings),
204 warnings, rcfile);
205 rc = -2;
206 }
207 }
208
209 if (!ispipe && !STAILQ_EMPTY(&mod_data->muttrc_stack))
210 {
211 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
212 STAILQ_REMOVE_HEAD(&mod_data->muttrc_stack, entries);
213 FREE(&np->data);
214 FREE(&np);
215 }
216
217 buf_pool_release(&linebuf);
218 return rc;
219}
220
227enum CommandResult parse_source(const struct Command *cmd, struct Buffer *line,
228 const struct ParseContext *pc, struct ParseError *pe)
229{
230 struct Buffer *err = pe->message;
231
232 if (!MoreArgs(line))
233 {
234 buf_printf(err, _("%s: too few arguments"), cmd->name);
235 return MUTT_CMD_WARNING;
236 }
237
238 struct Buffer *token = buf_pool_get();
239 struct Buffer *path = buf_pool_get();
241
242 do
243 {
244 if (parse_extract_token(token, line, TOKEN_BACKTICK_VARS) != 0)
245 {
246 buf_printf(err, _("source: error at %s"), line->dptr);
247 goto done;
248 }
249 buf_copy(path, token);
250 expand_path(path, false);
251
252 // Cheat: Remove the `const` so we can recurse
253 if (source_rc(buf_string(path), (struct ParseContext *) pc, pe) < 0)
254 {
255 buf_printf(err, _("source: file %s could not be sourced"), buf_string(path));
256 goto done;
257 }
258
259 } while (MoreArgs(line));
260
261 rc = MUTT_CMD_SUCCESS;
262
263done:
264 buf_pool_release(&path);
265 buf_pool_release(&token);
266 return rc;
267}
268
273{
275 if (mod_data)
276 mutt_list_free(&mod_data->muttrc_stack);
277}
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
295
296 struct Buffer *buf = buf_pool_get();
297 buf_strcpy(buf, line);
298 enum CommandResult ret = parse_rc_line(buf, pc, pe);
299 buf_pool_release(&buf);
300
301 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
302 STAILQ_REMOVE_HEAD(&mod_data->muttrc_stack, entries);
303 FREE(&np->data);
304 FREE(&np);
305
306 return ret;
307}
308
316{
318 struct ListNode *np = STAILQ_FIRST(&mod_data->muttrc_stack);
319 if (np && np->data)
320 return mutt_str_dup(np->data);
321
322 // stack is empty, return our own dummy file relative to cwd
323 struct Buffer *cwd = buf_pool_get();
324 mutt_path_getcwd(cwd);
325 buf_addstr(cwd, "/dummy.rc");
326 char *ret = buf_strdup(cwd);
327 buf_pool_release(&cwd);
328 return ret;
329}
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
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:31
@ TOKEN_BACKTICK_VARS
Expand variables within backticks.
Definition extract.h:57
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:227
#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:817
#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:228
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:665
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:586
#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:663
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:287
#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:272
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:315
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