NeoMutt  2025-12-11-596-g7cc1dd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
complete.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <dirent.h>
33#include <errno.h>
34#include <stdbool.h>
35#include <string.h>
36#include <sys/stat.h>
37#include "mutt/lib.h"
38#include "config/lib.h"
39#include "core/lib.h"
40#include "lib.h"
41#include "imap/lib.h"
42#include "nntp/lib.h"
43#include "globals.h"
44#include "muttlib.h"
45
46struct CompletionData;
47
58int mutt_complete(struct CompletionData *cd, struct Buffer *buf)
59{
60 const char *p = NULL;
61 DIR *dir = NULL;
62 struct dirent *de = NULL;
63 int init = 0;
64 size_t len;
65 struct Buffer *dirpart = NULL;
66 struct Buffer *exp_dirpart = NULL;
67 struct Buffer *filepart = NULL;
68 struct Buffer *tmp = NULL;
69 struct Buffer *imap_path = NULL;
70 int rc;
71
72 mutt_debug(LL_DEBUG2, "completing %s\n", buf_string(buf));
73
74 if (OptNews)
75 return nntp_complete(buf);
76
77 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
78 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
79
80 imap_path = buf_pool_get();
81 /* we can use '/' as a delimiter, imap_complete rewrites it */
82 char ch = buf_at(buf, 0);
83 if ((ch == '=') || (ch == '+') || (ch == '!'))
84 {
85 if (ch == '!')
86 p = NONULL(c_spool_file);
87 else
88 p = NONULL(c_folder);
89
90 buf_concat_path(imap_path, p, buf_string(buf) + 1);
91 }
92 else
93 {
94 buf_copy(imap_path, buf);
95 }
96
97 if (imap_path_probe(buf_string(imap_path), NULL) == MUTT_IMAP)
98 {
99 rc = imap_complete(buf, buf_string(imap_path));
100 buf_pool_release(&imap_path);
101 return rc;
102 }
103
104 buf_pool_release(&imap_path);
105
106 dirpart = buf_pool_get();
107 exp_dirpart = buf_pool_get();
108 filepart = buf_pool_get();
109 tmp = buf_pool_get();
110
111 ch = buf_at(buf, 0);
112 if ((ch == '=') || (ch == '+') || (ch == '!'))
113 {
114 buf_addch(dirpart, ch);
115 if (ch == '!')
116 buf_strcpy(exp_dirpart, c_spool_file);
117 else
118 buf_strcpy(exp_dirpart, c_folder);
119 p = strrchr(buf_string(buf), '/');
120 if (p)
121 {
122 buf_concatn_path(tmp, buf_string(exp_dirpart), buf_len(exp_dirpart),
123 buf_string(buf) + 1, (size_t) (p - buf_string(buf) - 1));
124 buf_copy(exp_dirpart, tmp);
125 buf_substrcpy(dirpart, buf_string(buf), p + 1);
126 buf_strcpy(filepart, p + 1);
127 }
128 else
129 {
130 buf_strcpy(filepart, buf_string(buf) + 1);
131 }
133 }
134 else
135 {
136 p = strrchr(buf_string(buf), '/');
137 if (p)
138 {
139 if (p == buf_string(buf)) /* absolute path */
140 {
141 p = buf_string(buf) + 1;
142 buf_strcpy(dirpart, "/");
143 buf_strcpy(filepart, p);
145 }
146 else
147 {
148 buf_substrcpy(dirpart, buf_string(buf), p);
149 buf_strcpy(filepart, p + 1);
150 buf_copy(exp_dirpart, dirpart);
151 expand_path(exp_dirpart, false);
153 }
154 }
155 else
156 {
157 /* no directory name, so assume current directory. */
158 buf_strcpy(filepart, buf_string(buf));
160 }
161 }
162
163 if (!dir)
164 {
165 mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n", buf_string(exp_dirpart),
166 strerror(errno), errno);
167 goto cleanup;
168 }
169
170 /* special case to handle when there is no filepart yet. find the first
171 * file/directory which is not "." or ".." */
172 len = buf_len(filepart);
173 if (len == 0)
174 {
175 while ((de = readdir(dir)))
176 {
177 if (!mutt_str_equal(".", de->d_name) && !mutt_str_equal("..", de->d_name))
178 {
179 buf_strcpy(filepart, de->d_name);
180 init++;
181 break;
182 }
183 }
184 }
185
186 while ((de = readdir(dir)))
187 {
188 if (mutt_strn_equal(de->d_name, buf_string(filepart), len))
189 {
190 if (init)
191 {
192 char *cp = filepart->data;
193
194 for (int i = 0; (*cp != '\0') && (de->d_name[i] != '\0'); i++, cp++)
195 {
196 if (*cp != de->d_name[i])
197 break;
198 }
199 *cp = '\0';
200 buf_fix_dptr(filepart);
201 }
202 else
203 {
204 struct stat st = { 0 };
205
206 buf_strcpy(filepart, de->d_name);
207
208 /* check to see if it is a directory */
209 if (buf_is_empty(dirpart))
210 {
211 buf_reset(tmp);
212 }
213 else
214 {
215 buf_copy(tmp, exp_dirpart);
216 buf_addch(tmp, '/');
217 }
218 buf_addstr(tmp, buf_string(filepart));
219 if ((stat(buf_string(tmp), &st) != -1) && (st.st_mode & S_IFDIR))
220 buf_addch(filepart, '/');
221 init = 1;
222 }
223 }
224 }
225 closedir(dir);
226
227 if (buf_is_empty(dirpart))
228 {
229 buf_copy(buf, filepart);
230 }
231 else
232 {
233 buf_copy(buf, dirpart);
234 if (!mutt_str_equal("/", buf_string(dirpart)) &&
235 (buf_string(dirpart)[0] != '=') && (buf_string(dirpart)[0] != '+'))
236 {
237 buf_addstr(buf, "/");
238 }
239 buf_addstr(buf, buf_string(filepart));
240 }
241
242cleanup:
243 buf_pool_release(&dirpart);
244 buf_pool_release(&exp_dirpart);
245 buf_pool_release(&filepart);
246 buf_pool_release(&tmp);
247
248 return init ? 0 : -1;
249}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
size_t buf_concatn_path(struct Buffer *buf, const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
Join a directory name and a filename.
Definition buffer.c:546
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
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
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition buffer.c:509
size_t buf_substrcpy(struct Buffer *buf, const char *beg, const char *end)
Copy a partial string into a Buffer.
Definition buffer.c:471
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
int mutt_complete(struct CompletionData *cd, struct Buffer *buf)
Attempt to complete a partial pathname.
Definition complete.c:58
Auto-completion.
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.
Convenience wrapper for the core headers.
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:539
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition file.h:63
bool OptNews
(pseudo) used to change reader mode
Definition globals.c:53
Global variables.
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2546
IMAP network mailbox.
int imap_complete(struct Buffer *buf, const char *path)
Try to complete an IMAP folder path.
Definition imap.c:1462
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
Convenience wrapper for the library headers.
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition string.c:429
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:121
Some miscellaneous functions.
int nntp_complete(struct Buffer *buf)
Auto-complete NNTP newsgroups.
Definition complete.c:46
Usenet network mailbox type; talk to an NNTP server.
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 NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
State data for auto-completion.
Definition data.h:32
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49