NeoMutt  2021-10-29-33-g41675a
Teaching an old dog new tricks
DOXYGEN
complete.c File Reference

String auto-completion routines. More...

#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "muttlib.h"
#include "options.h"
#include "protos.h"
#include "imap/lib.h"
#include "nntp/lib.h"
+ Include dependency graph for complete.c:

Go to the source code of this file.

Functions

int mutt_complete (char *buf, size_t buflen)
 Attempt to complete a partial pathname. More...
 

Detailed Description

String auto-completion routines.

Authors
  • Michael R. Elkins

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file complete.c.

Function Documentation

◆ mutt_complete()

int mutt_complete ( char *  buf,
size_t  buflen 
)

Attempt to complete a partial pathname.

Parameters
bufBuffer containing pathname
buflenLength of buffer
Return values
0Ok
-1No matches

Given a partial pathname, fill in as much of the rest of the path as is unique.

Definition at line 57 of file complete.c.

58 {
59  const char *p = NULL;
60  DIR *dirp = NULL;
61  struct dirent *de = NULL;
62  int init = 0;
63  size_t len;
64  struct Buffer *dirpart = NULL;
65  struct Buffer *exp_dirpart = NULL;
66  struct Buffer *filepart = NULL;
67  struct Buffer *tmp = NULL;
68 #ifdef USE_IMAP
69  struct Buffer *imap_path = NULL;
70  int rc;
71 #endif
72 
73  mutt_debug(LL_DEBUG2, "completing %s\n", buf);
74 
75 #ifdef USE_NNTP
76  if (OptNews)
77  return nntp_complete(buf, buflen);
78 #endif
79 
80  const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
81  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
82 #ifdef USE_IMAP
83  imap_path = mutt_buffer_pool_get();
84  /* we can use '/' as a delimiter, imap_complete rewrites it */
85  if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
86  {
87  if (*buf == '!')
88  p = NONULL(c_spool_file);
89  else
90  p = NONULL(c_folder);
91 
92  mutt_buffer_concat_path(imap_path, p, buf + 1);
93  }
94  else
95  mutt_buffer_strcpy(imap_path, buf);
96 
97  if (imap_path_probe(mutt_buffer_string(imap_path), NULL) == MUTT_IMAP)
98  {
99  rc = imap_complete(buf, buflen, mutt_buffer_string(imap_path));
100  mutt_buffer_pool_release(&imap_path);
101  return rc;
102  }
103 
104  mutt_buffer_pool_release(&imap_path);
105 #endif
106 
107  dirpart = mutt_buffer_pool_get();
108  exp_dirpart = mutt_buffer_pool_get();
109  filepart = mutt_buffer_pool_get();
110  tmp = mutt_buffer_pool_get();
111 
112  if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
113  {
114  mutt_buffer_addch(dirpart, *buf);
115  if (*buf == '!')
116  mutt_buffer_strcpy(exp_dirpart, NONULL(c_spool_file));
117  else
118  mutt_buffer_strcpy(exp_dirpart, NONULL(c_folder));
119  p = strrchr(buf, '/');
120  if (p)
121  {
123  mutt_buffer_len(exp_dirpart), buf + 1,
124  (size_t) (p - buf - 1));
125  mutt_buffer_copy(exp_dirpart, tmp);
126  mutt_buffer_substrcpy(dirpart, buf, p + 1);
127  mutt_buffer_strcpy(filepart, p + 1);
128  }
129  else
130  mutt_buffer_strcpy(filepart, buf + 1);
131  dirp = opendir(mutt_buffer_string(exp_dirpart));
132  }
133  else
134  {
135  p = strrchr(buf, '/');
136  if (p)
137  {
138  if (p == buf) /* absolute path */
139  {
140  p = buf + 1;
141  mutt_buffer_strcpy(dirpart, "/");
142  mutt_buffer_strcpy(filepart, p);
143  dirp = opendir(mutt_buffer_string(dirpart));
144  }
145  else
146  {
147  mutt_buffer_substrcpy(dirpart, buf, p);
148  mutt_buffer_strcpy(filepart, p + 1);
149  mutt_buffer_copy(exp_dirpart, dirpart);
150  mutt_buffer_expand_path(exp_dirpart);
151  dirp = opendir(mutt_buffer_string(exp_dirpart));
152  }
153  }
154  else
155  {
156  /* no directory name, so assume current directory. */
157  mutt_buffer_strcpy(filepart, buf);
158  dirp = opendir(".");
159  }
160  }
161 
162  if (!dirp)
163  {
164  mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n",
165  mutt_buffer_string(exp_dirpart), strerror(errno), errno);
166  goto cleanup;
167  }
168 
169  /* special case to handle when there is no filepart yet. find the first
170  * file/directory which is not "." or ".." */
171  len = mutt_buffer_len(filepart);
172  if (len == 0)
173  {
174  while ((de = readdir(dirp)))
175  {
176  if (!mutt_str_equal(".", de->d_name) && !mutt_str_equal("..", de->d_name))
177  {
178  mutt_buffer_strcpy(filepart, de->d_name);
179  init++;
180  break;
181  }
182  }
183  }
184 
185  while ((de = readdir(dirp)))
186  {
187  if (mutt_strn_equal(de->d_name, mutt_buffer_string(filepart), len))
188  {
189  if (init)
190  {
191  char *cp = filepart->data;
192 
193  for (int i = 0; (*cp != '\0') && (de->d_name[i] != '\0'); i++, cp++)
194  {
195  if (*cp != de->d_name[i])
196  break;
197  }
198  *cp = '\0';
199  mutt_buffer_fix_dptr(filepart);
200  }
201  else
202  {
203  struct stat st = { 0 };
204 
205  mutt_buffer_strcpy(filepart, de->d_name);
206 
207  /* check to see if it is a directory */
208  if (mutt_buffer_is_empty(dirpart))
209  {
210  mutt_buffer_reset(tmp);
211  }
212  else
213  {
214  mutt_buffer_copy(tmp, exp_dirpart);
215  mutt_buffer_addch(tmp, '/');
216  }
217  mutt_buffer_addstr(tmp, mutt_buffer_string(filepart));
218  if ((stat(mutt_buffer_string(tmp), &st) != -1) && (st.st_mode & S_IFDIR))
219  mutt_buffer_addch(filepart, '/');
220  init = 1;
221  }
222  }
223  }
224  closedir(dirp);
225 
226  if (!mutt_buffer_is_empty(dirpart))
227  {
228  mutt_str_copy(buf, mutt_buffer_string(dirpart), buflen);
229  if (!mutt_str_equal("/", mutt_buffer_string(dirpart)) &&
230  (mutt_buffer_string(dirpart)[0] != '=') && (mutt_buffer_string(dirpart)[0] != '+'))
231  {
232  mutt_str_copy(buf + strlen(buf), "/", buflen - strlen(buf));
233  }
234  mutt_str_copy(buf + strlen(buf), mutt_buffer_string(filepart), buflen - strlen(buf));
235  }
236  else
237  mutt_str_copy(buf, mutt_buffer_string(filepart), buflen);
238 
239 cleanup:
240  mutt_buffer_pool_release(&dirpart);
241  mutt_buffer_pool_release(&exp_dirpart);
242  mutt_buffer_pool_release(&filepart);
244 
245  return init ? 0 : -1;
246 }
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_substrcpy(struct Buffer *buf, const char *beg, const char *end)
Copy a partial string into a Buffer.
Definition: buffer.c:342
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
size_t mutt_buffer_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:411
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:445
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2402
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int imap_complete(char *buf, size_t buflen, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1343
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:53
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:715
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:404
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:560
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
int nntp_complete(char *buf, size_t buflen)
Auto-complete NNTP newsgroups.
Definition: complete.c:47
bool OptNews
(pseudo) used to change reader mode
Definition: options.h:50
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define NONULL(x)
Definition: string2.h:37
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function: